From b7db574ee08a9bdfc1310752822260149fd8e239 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 7 Jun 2018 14:43:21 +0100 Subject: [PATCH 001/177] Update Gemfile.lock --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 75cee139..bc94a2e3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -21,7 +21,7 @@ GEM commonmarker (0.17.9) ruby-enum (~> 0.5) concurrent-ruby (1.0.5) - eip_validator (0.4.0) + eip_validator (0.8.0) activemodel front_matter_parser (~> 0.1.1) ethon (0.11.0) @@ -257,7 +257,7 @@ PLATFORMS ruby DEPENDENCIES - eip_validator (>= 0.4.0) + eip_validator (>= 0.8.0) github-pages html-proofer (>= 3.3.1) jekyll (~> 3.6.2) From 797c994872c4ffcb834e8d0c75f1dd1300b781b4 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 7 Jun 2018 14:59:14 +0100 Subject: [PATCH 002/177] Update eip-689.md --- EIPS/eip-689.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-689.md b/EIPS/eip-689.md index fe193939..5b6935e9 100644 --- a/EIPS/eip-689.md +++ b/EIPS/eip-689.md @@ -2,7 +2,7 @@ eip: 689 title: Address Collision of Contract Address Causes Exceptional Halt author: Yoichi Hirai -type: Standard Track +type: Standards Track category: Core status: Draft created: 2017-08-15 From 3b05be29e0d9896ae20fff99fe54c61325687502 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 7 Jun 2018 14:59:32 +0100 Subject: [PATCH 003/177] Update eip-884.md --- EIPS/eip-884.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-884.md b/EIPS/eip-884.md index ecefd78f..37ca1aa1 100644 --- a/EIPS/eip-884.md +++ b/EIPS/eip-884.md @@ -2,7 +2,7 @@ eip: 884 title: 'DGCL Token' author: 'Dave Sag ' -type: Standard Track +type: Standards Track category: ERC status: Draft created: 2018-02-14 From 3406f36c7a2714f85e2606e551aa59373dde7182 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 7 Jun 2018 15:02:39 +0100 Subject: [PATCH 004/177] Only validate EIP files with the correct filenames --- .travis-ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis-ci.sh b/.travis-ci.sh index a4ee02c6..6851449f 100755 --- a/.travis-ci.sh +++ b/.travis-ci.sh @@ -22,6 +22,6 @@ elif [[ $TASK = 'eip-validator' ]]; then exit 1 fi - FILES=$(ls EIPS/*.md) + FILES="$(ls EIPS/*.md | egrep "^eip-[0-9]+.md$")" bundle exec eip_validator $FILES fi From 5398585fd59c91c24fcf120f69b98f83712907ac Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 7 Jun 2018 15:14:33 +0100 Subject: [PATCH 005/177] Fix .travis-ci.sh --- .travis-ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis-ci.sh b/.travis-ci.sh index 6851449f..63f5d085 100755 --- a/.travis-ci.sh +++ b/.travis-ci.sh @@ -22,6 +22,6 @@ elif [[ $TASK = 'eip-validator' ]]; then exit 1 fi - FILES="$(ls EIPS/*.md | egrep "^eip-[0-9]+.md$")" + FILES="$(ls EIPS/*.md | egrep "eip-[0-9]+.md")" bundle exec eip_validator $FILES fi From 56be501bab82ca95f5c03edd4dc6717bdadd8935 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 7 Jun 2018 16:29:57 -0700 Subject: [PATCH 006/177] Automatically merged updates to draft EIP(s) 1123 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing draft EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1123.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-1123.md b/EIPS/eip-1123.md index 2a96d3b1..3ef991f5 100644 --- a/EIPS/eip-1123.md +++ b/EIPS/eip-1123.md @@ -49,8 +49,7 @@ This version: - Generalizes storage URIs to represent any content addressable URI scheme, not only IPFS. -- Renames *release lockfile* to *package manifest*, or *package* for - short. +- Renames *release lockfile* to *package manifest*. - Adds support for languages other than Solidity by generalizing the compiler information format. @@ -117,10 +116,10 @@ document are to be interpreted as described in RFC 2119. ### Prefixed vs Unprefixed -A [prefixed](#term-prefixed) hexadecimal value begins with `'0x'`. +A [prefixed](#term-prefixed) hexadecimal value begins with `0x`. [Unprefixed](#term-unprefixed) values have no prefix. Unless otherwise specified, all hexadecimal values **should** be represented with the -`'0x'` prefix. +`0x` prefix. @@ -225,7 +224,7 @@ document conforms to. Packages **must** include this field. The `package_name` field defines a human readable name for this package. Packages **must** include this field. Package names **must** begin with a lowercase letter and be comprised of only lowercase letters, numeric -characters, and the dash character `'-'`. Package names **must** not +characters, and the dash character `-`. Package names **must** not exceed 214 characters in length.
@@ -639,8 +638,8 @@ encoded when [linking](#term-linking) the corresponding bytecode. - +

Allowed Values

'literal' for bytecode literals

-

'reference' for named references to a particular Contract Instance

"literal" for bytecode literals

+

"reference" for named references to a particular Contract Instance

@@ -1434,7 +1433,7 @@ nightly should be denoted in the form of `-` ex: #### Settings: `settings` The `settings` field defines any settings or configuration that was used -in compilation. For the `'solc'` compiler, this **should** conform to +in compilation. For the `"solc"` compiler, this **should** conform to the [Compiler Input and Output Description](http://solidity.readthedocs.io/en/latest/using-the-compiler.html#compiler-input-and-output-json-description). @@ -1558,7 +1557,7 @@ Bytecode The set of EVM instructions as produced by a compiler. Unless otherwise specified this should be assumed to be hexadecimal encoded, representing -a whole number of bytes, and [prefixed](#term-prefixed) with `'0x'`. +a whole number of bytes, and [prefixed](#term-prefixed) with `0x`. Bytecode can either be linked or unlinked. (see [Linking](#term-linking)) @@ -1793,7 +1792,7 @@ for package manifests.) Prefixed -------- -[Bytecode](#term-bytecode) string with leading `'0x'`. +[Bytecode](#term-bytecode) string with leading `0x`. From 5bec04d8ef403a5e3ac5ce76cb4a13092d6284d6 Mon Sep 17 00:00:00 2001 From: Leo Arias Date: Thu, 7 Jun 2018 20:34:47 -0600 Subject: [PATCH 007/177] Automatically merged updates to draft EIP(s) 1066 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing draft EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1066.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-1066.md b/EIPS/eip-1066.md index a4e68e85..c4865c98 100644 --- a/EIPS/eip-1066.md +++ b/EIPS/eip-1066.md @@ -93,15 +93,15 @@ function safeIncrement(uint8 interval) public returns (byte status, uint8 newCou } ``` -In the rare case that there a multiple codes required to express an idea, -they should be organized in asending order. +In the rare case that there are multiple codes required to express an idea, +they should be organized in ascending order. ### Code Table Codes break nicely into a 16x16 matrix, represented as a 2-digit hex number. The high nibble represents the code's kind or "category", and the low nibble contains the state or "reason". We present them below as separate tables per range for -explanitory and layout reasons. +explanatory and layout reasons. Unspecified codes are _not_ free for arbitrary use, but rather open for further specification. @@ -178,7 +178,7 @@ Data lookups and order matching are two common use cases. #### Negotiation, Terms, and Offers Negotiation, and very broadly the flow of such transactions. -Note that "other party" may be more than one actor (not nessesarily the sender). +Note that "other party" may be more than one actor (not necessarily the sender). | Code | Description | |-----------------|:----------------------------| From cd530aea370d80eb2df186413c7e2dba7191a61d Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Fri, 8 Jun 2018 06:49:03 -0700 Subject: [PATCH 008/177] EIP 173: Contract Ownership Standard (#1142) * Create eip-173.md * Removed "review-period-end" --- EIPS/eip-173.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 EIPS/eip-173.md diff --git a/EIPS/eip-173.md b/EIPS/eip-173.md new file mode 100644 index 00000000..eb02d616 --- /dev/null +++ b/EIPS/eip-173.md @@ -0,0 +1,108 @@ +--- +eip: 173 +title: ERC-173 Contract Ownership Standard +author: Nick Mudge , Dan Finlay +type: Standards Track +category: ERC +status: Draft +created: 2018-06-07 +--- + +## Simple Summary + +A standard interface for ownership of contracts. + +## Abstract + +The following standard allows for the implementation of a standard API for getting the owner address of a contract and transferring contract ownership to a different address. + +Key factors influencing the standard: +- Keeping the number of functions in the interface to a minimum to prevent contract bloat. +- Backwards compatibility with existing contracts. +- Simplicity +- Gas efficient + +## Motivation + +Many smart contracts require that they be owned or controlled in some way. For example to withdraw funds or perform administrative actions. It is so common that the contract interface used to handle contract ownership should be standardized to allow compatibility with contracts that manage contracts. + +Here are some examples of kinds of contracts and applications that can benefit from this standard: +1. Exchanges that buy/sell/auction ethereum contracts. This is only widely possible if there is a standard for getting the owner of a contract and transferring ownership. +2. Contract wallets that hold the ownership of contracts and that can transfer the ownership of contracts. +3. Contract registries. It makes sense for some registries to only allow the owners of contracts to add/remove their contracts. A standard must exist for these contract registries to verify that a contract is being submitted by the owner of it before accepting it. + +## 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 ERC-173 compliant contract must implement the `ERC173` interface. Contracts that inherit from OpenZeppelin's `Ownable` contract are compliant with ERC-173. However future contracts should also implement `ERC165` for the ERC-173 interface. + +```solidity +pragma solidity ^0.4.24; + +/// @title ERC-173 Contract Ownership Standard +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-173.md +/// Note: the ERC-165 identifier for this interface is 0x7f5828d0 +interface ERC173 /* is ERC165 */ { + /// @dev This emits when ownership of a contract changes. + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /// @notice Get the address of the owner + /// @return The address of the owner. + function owner() view external; + + /// @notice Get the owner of a contract + /// @param _newOwner The address of the new owner of the contract + function transferOwnership(address _newOwner) external; +} + +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); +} +``` + +The `owner()` function may be implemented as `pure` or `view`. + +The `transferOwnership(address _newOwner)` function may be implemented as `public` or `external`. + +## Rationale + +Several ownership schemes were considered. The scheme chosen in this standard was chosen because of its simplicity, low gas cost and backwards compatibility with OpenZeppelin's implementation of the Ownable contract which is in active use. + +Here are other schemes that were considered: +1. **Associating an Ethereum Name Service (ENS) domain name with a contract.** A contract's `owner()` function could look up the owner address of a particular ENS name and use that as the owning address of the contract. Using this scheme a contract could be transferred by transferring the ownership of the ENS domain name to a different address. Short comings to this approach are that it is not backwards compatible with existing contracts and requires gas to make external calls to ENS related contracts to get the owner address. +2. **Associating an ERC721-based non-fungible token (NFT) with a contract.** Ownership of a contract could be tied to the ownership of an NFT. The benefit of this approach is that the existing ERC721-based infrastructure could be used to sell/buy/auction contracts. Short comings to this approach are additional complexity and infrastructure required. A contract could be associated with a particular NFT but the NFT would not track that it had ownership of a contract unless it was programmed to track contracts. In addition handling ownership of contracts this way is not backwards compatible. + +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. + +## Backwards Compatibility + +OpenZeppelin's Ownable contract is actively being used in contracts. Contracts that inherit Ownable are in compliance with this standard. + +However future contracts should also implement `ERC165` for the ERC-173 interface. + +## Implementations + +OpenZeppelin's implementation is here: https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + + + + + + + + + + + + From c8c48a7ed1827b1908faaff6e804ab5bb46977ac Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Fri, 8 Jun 2018 22:49:24 -0700 Subject: [PATCH 009/177] Automatically merged updates to draft EIP(s) 173 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing draft EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-173.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-173.md b/EIPS/eip-173.md index eb02d616..8c88f1bc 100644 --- a/EIPS/eip-173.md +++ b/EIPS/eip-173.md @@ -2,6 +2,7 @@ eip: 173 title: ERC-173 Contract Ownership Standard author: Nick Mudge , Dan Finlay +discussions-to: https://github.com/ethereum/EIPs/issues/173 type: Standards Track category: ERC status: Draft @@ -51,7 +52,7 @@ interface ERC173 /* is ERC165 */ { /// @return The address of the owner. function owner() view external; - /// @notice Get the owner of a contract + /// @notice Set the address of the new owner of the contract /// @param _newOwner The address of the new owner of the contract function transferOwnership(address _newOwner) external; } @@ -71,6 +72,8 @@ The `owner()` function may be implemented as `pure` or `view`. The `transferOwnership(address _newOwner)` function may be implemented as `public` or `external`. +The OwnershipTransferred event does not have to be emitted when a contract is created. + ## Rationale Several ownership schemes were considered. The scheme chosen in this standard was chosen because of its simplicity, low gas cost and backwards compatibility with OpenZeppelin's implementation of the Ownable contract which is in active use. From d686a655de7bcee5f05508bb0e98204158b231d6 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Sat, 9 Jun 2018 12:19:15 -0700 Subject: [PATCH 010/177] [WIP] Add eth_signTypedData as a standard for machine-verifiable and human-readable typed data signing with Ethereum keys (#712) * Add eip-signTypedData * Change namespace from personal to eth * Change a way schema hash is combined together with data as proposed by @MicahZoltu * Add a note about it being implemented in MetaMask as an experimental feature * Add signerAddress as a parameter * Add test vectors * Fix an example * Missing commas, periods * Address the feedback * Add a missing signerAddress parameter in the example * Change the order of parameters to have an address as a second arg * Wrote motivation * WIP * First draft of specification * Fixes * Update to new EIP format * Assign EIP number * Clarify encoding of short static byte arrays * Removed Solidity changes * Fixup * Fix typos * WIP EIP191 * WIP TODO * WIP Replay attacks * Fixes the sorted by name example encoding * Remove Solidity hash * Added note on replay protection * Redesign domain separator * Include images and simple motivation * Fix up EIP metadata formatting * Add domain separator * Remove replay attacks from todo list * Add Jacob Evans to authors * Clarify encodeData * Rename Message example to Mail * Update mock signing screen * Rework EIP712Domain * Update Solidity example * Update Javascript example * Relocate files * Rename DomainSeparator to EIP712Domain (fix) * Move examples to separate files * Remove httpOrigin domain parameter * Update JSON-Schema * Add registery of version bytes * Add eip712 to eip191 registery * Add requires header * Set correct language on all snipets * GitHub highlighting for Solidity files * Update Web3 API specification * Use abi.encode where possible * Update JSON-RPC specification * Asset path repo is ethereums * Correctly spelling of registry --- .gitattributes | 3 + EIPS/eip-191.md | 11 + EIPS/eip-712.md | 446 +++++++++++++++++++++++++++ assets/eip-712/Example.js | 148 +++++++++ assets/eip-712/Example.sol | 106 +++++++ assets/eip-712/eth_sign.png | Bin 0 -> 132226 bytes assets/eip-712/eth_signTypedData.png | Bin 0 -> 112451 bytes 7 files changed, 714 insertions(+) create mode 100644 .gitattributes create mode 100644 EIPS/eip-712.md create mode 100644 assets/eip-712/Example.js create mode 100644 assets/eip-712/Example.sol create mode 100644 assets/eip-712/eth_sign.png create mode 100644 assets/eip-712/eth_signTypedData.png diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..98eb0e8e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# GitHub highlighting for Solidity files +# See https://github.com/github/linguist/pull/3973#issuecomment-357507741 +*.sol linguist-language=Solidity diff --git a/EIPS/eip-191.md b/EIPS/eip-191.md index 76ca6fe0..63b53ea5 100644 --- a/EIPS/eip-191.md +++ b/EIPS/eip-191.md @@ -46,6 +46,17 @@ Additionally, `0x19` has been chosen because since ethereum/go-ethereum#2940 , t Using `0x19` thus makes it possible to extend the scheme by defining a version `0x45` (`E`) to handle these kinds of signatures. +### Registry of version bytes + +| Version byte | EIP | Description +| ------------ | -------------- | ----------- +| `0x00` | [191][eip-191] | Data with intended validator +| `0x01` | [712][eip-712] | Structured data +| `0x45` | [191][eip-191] | `personal_sign` messages + +[eip-191]: https://eips.ethereum.org/EIPS/eip-191 +[eip-712]: https://eips.ethereum.org/EIPS/eip-712 + ### Example function submitTransactionPreSigned(address destination, uint value, bytes data, uint nonce, uint8 v, bytes32 r, bytes32 s) diff --git a/EIPS/eip-712.md b/EIPS/eip-712.md new file mode 100644 index 00000000..eb42486b --- /dev/null +++ b/EIPS/eip-712.md @@ -0,0 +1,446 @@ +--- +eip: 712 +title: Ethereum typed structured data hashing and signing +author: Remco Bloemen , + Leonid Logvinov , + Jacob Evans +discussions-to: https://ethereum-magicians.org/t/eip-712-eth-signtypeddata-as-a-standard-for-machine-verifiable-and-human-readable-typed-data-signing/397 +status: Draft +type: Standards Track +category: Interface +created: 2017-09-12 +requires: 155, 191 +--- + + + +## Simple Summary + + + +Signing data is a solved problem if all we care about are bytestrings. Unfortunately in the real world we care about complex meaningful messages. Hashing structured data is non-trivial and errors result in loss of the security properties of the system. + +As such, the adage "don't roll your own crypto" applies. Instead, a peer-reviewed well-tested standard method needs to be used. This EIP aims to be that standard. + +## Abstract + + + +This is a standard for hashing and signing of typed structured data as opposed to just bytestrings. It includes a + +* theoretical framework for correctness of encoding functions, +* specification of structured data similar to and compatible with Solidity structs, +* safe hashing algorithm for instances of those structures, +* safe inclusion of those instances in the set of signable messages, +* an extensible mechanism for domain separation, +* new RPC call `eth_signTypedData`, and +* an optimized implementation of the hashing algorithm in EVM. + +It does not include replay protection. + +## Motivation + + + +This EIP aims to improve the usability of off-chain message signing for use on-chain. We are seeing growing adoption of off-chain message signing as it saves gas and reduces the number of transactions on the blockchain. Currently signed messages are an opaque hex string displayed to the user with little context about the items that make up the message. + + + +Here we outline a scheme to encode data along with its structure which allows it to be displayed to the user for verification when signing. Below is an example of what a user could be shown when signing an EIP712 message. + + + +### Signatures and Hashing overview + +A signature scheme consists of hashing algorithm and a signing algorithm. The signing algorithm of choice in Ethereum is `secp256k1`. The hashing algorithm of choice is `keccak256`, this is a function from bytestrings, 𝔹⁸ⁿ, to 256-bit strings, 𝔹²⁵⁶. + +A good hashing algorithm should satisfy security properties such as determinism, second pre-image resistance and collision resistance. The `keccak256` function satisfies the above criteria _when applied to bytestrings_. If we want to apply it to other sets we first need to map this set to bytestrings. It is critically important that this encoding function is [deterministic][deterministic] and [injective][injective]. If it is not deterministic then the hash might differ from the moment of signing to the moment of verifying, causing the signature to incorrectly be rejected. If it is not injective then there are two different elements in our input set that hash to the same value, causing a signature to be valid for a different unrelated message. + +[deterministic]: https://en.wikipedia.org/wiki/Deterministic_algorithm +[injective]: https://en.wikipedia.org/wiki/Injective_function + +### Transactions and bytestrings + +An illustrative example of the above breakage can be found in Ethereum. Ethereum has two kinds of messages, transactions `𝕋` and bytestrings `𝔹⁸ⁿ`. These are signed using `eth_sendTransaction` and `eth_sign` respectively. Originally the encoding function `encode : 𝕋 ∪ 𝔹⁸ⁿ → 𝔹⁸ⁿ` was as defined as follows: + +* `encode(t : 𝕋) = RLP_encode(t)` +* `encode(b : 𝔹⁸ⁿ) = b` + +While individually they satisfy the required properties, together they do not. If we take `b = RLP_encode(t)` we have a collision. This is mitigated in Geth [PR 2940][geth-pr] by modifying the second leg of the encoding function: + +[geth-pr]: https://github.com/ethereum/go-ethereum/pull/2940 + +* `encode(b : 𝔹⁸ⁿ) = "\x19Ethereum Signed Message:\n" ‖ len(b) ‖ b` where `len(b)` is the ascii-decimal encoding of the number of bytes in `b`. + +This solves the collision between the legs since `RLP_encode(t : 𝕋)` never starts with `\x19`. There is still the risk of the new encoding function not being deterministic or injective. It is instructive to consider those in detail. + +As is, the definition above is not deterministic. For a 4-byte string `b` both encodings with `len(b) = "4"` and `len(b) = "004"` are valid. This can be solved by further requiring that the decimal encoding of the length has no leading zeros and `len("") = "0"`. + +The above definition is not obviously collision free. Does a bytestring starting with `"\x19Ethereum Signed Message:\n42a…"` mean a 42-byte string starting with `a` or a 4-byte string starting with `2a`?. This was pointed out in [Geth issue #14794][geth-issue-14794] and motivated Trezor to [not implement the standard][trezor] as-is. Fortunately this does not lead to actual collisions as the total length of the encoded bytestring provides sufficient information to disambiguate the cases. + +[geth-issue-14794]: https://github.com/ethereum/go-ethereum/issues/14794 +[trezor]: https://github.com/trezor/trezor-mcu/issues/163 + +Both determinism and injectiveness would be trivially true if `len(b)` was left out entirely. The point is, it is difficult to map arbitrary sets to bytestrings without introducing security issues in the encoding function. Yet the current design of `eth_sign` still takes a bytestring as input and expects implementors to come up with an encoding. + +### Arbitrary messages + +The `eth_sign` call assumes messages to be bytestrings. In practice we are not hashing bytestrings but the collection of all semantically different messages of all different DApps `𝕄`. Unfortunately, this set is impossible to formalize. Instead we approximate it with the set of typed named structures `𝕊`. This standard formalizes the set `𝕊` and provides a deterministic injective encoding function for it. + +Just encoding structs is not enough. It is likely that two different DApps use identical structs. When this happens, a signed message intended for one DApp would also be valid for the other. The signatures are compatible. This can be intended behaviour, in which case everything is fine as long as the DApps took replay attacks into consideration. If it is not intended, there is a security problem. + +The way to solve this is by introducing a domain separator, a 256-bit number. This is a value unique to each domain that is 'mixed in' the signature. It makes signatures from different domains incompatible. The domain separator is designed to include bits of DApp unique information such as the name of the DApp, the intended validator contract address, the expected DApp domain name, etc. The user and user-agent can use this information to mitigate phishing attacks, where a malicious DApp tries to trick the user into signing a message for another DApp. + +### Note on replay attacks + +This standard is only about signing messages and verifying signatures. In many practical applications, signed messages are used to authorize an action, for example an exchange of tokens. It is _very important_ that implementers make sure the application behaves correctly when it sees the same signed message twice. For example, the repeated message should be rejected or the authorized action should be idempotent. How this is implemented is specific to the application and out of scope for this standard. + +## Specification + + + +The set of signable messages is extended from transactions and bytestrings `𝕋 ∪ 𝔹⁸ⁿ` to also include structured data `𝕊`. The new set of signable messages is thus `𝕋 ∪ 𝔹⁸ⁿ ∪ 𝕊`. They are encoded to bytestrings suitable for hashing and signing as follows: + +* `encode(transaction : 𝕋) = RLP_encode(transaction)` +* `encode(message : 𝔹⁸ⁿ) = "\x19Ethereum Signed Message:\n" ‖ len(message) ‖ message` where `len(message)` is the _non-zero-padded_ ascii-decimal encoding of the number of bytes in `message`. +* `encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)` where `domainSeparator` and `hashStruct(message)` are defined below. + +This encoding is deterministic because the individual components are. The encoding is injective because the three cases always differ in first byte. (`RLP_encode(transaction)` does not start with `\x19`.) + +The encoding is compliant with [EIP-191][eip191]. The 'version byte' is fixed to `0x01`, the 'version specific data' is the 32-byte domain separator `domainSeparator` and the 'data to sign' is the 32-byte `hashStruct(message)`. + +[eip191]: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-191.md + +### Definition of typed structured data `𝕊` + +To define the set of all structured data, we start with defining acceptable types. Like ABIv2 these are closely related to Solidity types. It is illustrative to adopt Solidity notation to explain the definitions. The standard is specific to the Ethereum Virtual Machine, but aims to be agnostic to higher level languages. Example: + +```Solidity +struct Mail { + address from; + address to; + string contents; +} +``` + +**Definition**: A _struct type_ has valid identifier as name and contains zero or more member variables. Member variables have a member type and a name. + +**Definition**: A _member type_ can be either an atomic type, a dynamic type or a reference type. + +**Definition**: The _atomic types_ are `bytes1` to `bytes32`, `uint8` to `uint256`, `int8` to `int256`, `bool` and `address`. These correspond to their definition in Solidity. Note that there are no aliases `uint` and `int`. Note that contract addresses are always plain `address`. Fixed point numbers are not supported by the standard. Future versions of this standard may add new atomic types. + +**Definition**: The _dynamic types_ are `bytes` and `string`. These are like the atomic types for the purposed of type declaration, but their treatment in encoding is different. + +**Definition**: The _reference types_ are arrays and structs. Arrays are either fixed size or dynamic and denoted by `Type[n]` or `Type[]` respectively. Structs are references to other structs by their name. The standard supports recursive struct types. + +**Definition**: The set of structured typed data `𝕊` contains all the instances of all the struct types. + +### Definition of `hashStruct` + +The `hashStruct` function is defined as + +* `hashStruct(s : 𝕊) = keccak256(typeHash ‖ encodeData(s))` where `typeHash = keccak256(encodeType(typeOf(s)))` + +**Note**: The `typeHash` is a constant for a given struct type and does not need to be runtime computed. + +### Definition of `encodeType` + +The type of a struct is encoded as `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"` where each member is written as `type ‖ " " ‖ name`. For example, the above `Mail` struct is encoded as `Mail(address from,address to,string contents)`. + +If the struct type references other struct types (and these in turn reference even more struct types), then the set of referenced struct types is collected, sorted by name and appended to the encoding. An example encoding is `Transaction(Person from,Person to,Asset tx)Asset(address token,uint256 amount)Person(address wallet,string name)`. + +### Definition of `encodeData` + +The encoding of a struct instance is `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`, i.e. the concatenation of the encoded of the member values in the order that they apear in the type. Each encoded member value is exactly 32-byte long. + +The atomic values are encoded as follows: Boolean `false` and `true` are encoded as `uint256` values `0` and `1` respectively. Addresses are encoded as `uint160`. Integer values are sign-extended to 256-bit and encoded in big endian order. `bytes1` to `bytes31` are arrays with a beginning (index `0`) and an end (index `length - 1`), they are zero-padded at the end to `bytes32` and encoded in beginning to end order. This corresponds to their encoding in ABI v1 and v2. + +The dynamic values `bytes` and `string` are encoded as a `keccak256` hash of their contents. + +The array values are encoded as the `keccak256` hash of the concatenated `encodeData` of their contents (i.e. the encoding of `SomeType[5]` is identical to that of a struct containing five members of type `SomeType`). + +The struct values are encoded recursively as `hashStruct(value)`. This is undefined for cyclical data. + +### Definition of `domainSeparator` + + +```Solidity +domainSeparator = hashStruct(eip712Domain) +``` + +where the type of `eip712Domain` is a struct named `EIP712Domain` with one or more of the below fields. Protocol designers only need to include the fields that make sense for their signing domain. Unused fields are left out of the struct type. + +* `string name` the user readable name of signing domain, i.e. the name of the DApp or the protocol. +* `string version` the current major version of the signing domain. Signatures from different versions are not compatible. +* `uint256 chainId` the [EIP-155][eip155] chain id. The user-agent *should* refuse signing if it does not match the currently active chain. +* `address verifyingContract` the address of the contract that will verify the signature. The user-agent *may* do contract specific phishing prevention. +* `bytes32 salt` an disambiguating salt for the protocol. This can be used as a domain separator of last resort. + +[eip155]: https://eips.ethereum.org/EIPS/eip-155 + +Future extensions to this standard can add new fields with new user-agent behaviour constraints. User-agents are free to use the provided information to inform/warn users or refuse signing. + +### Specification of the `eth_signTypedData` JSON RPC + +The method `eth_signTypedData` is added to the [Ethereum JSON-RPC][json-rpc]. The method parallels `eth_sign`. + +[json-rpc]: https://github.com/ethereum/wiki/wiki/JSON-RPC + +#### eth_signTypedData + +The sign method calculates an Ethereum specific signature with: `sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))`. + +By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim. + +**Note** the address to sign with must be unlocked. + +##### Parameters + +1. `Address` - 20 Bytes - Address of the account that will sign the messages. +2. `TypedData` - Typed structured data to be signed. + +Typed data is a JSON object containing type information, domain seprator parameters and the message object. Below is the [json-schema][jsons] definition for `TypedData` param. + +[jsons]: http://json-schema.org/ + +```JavaScript +{ + type: 'object', + properties: { + types: { + type: 'object', + additionalProperties: { + type: 'array', + items: { + type: 'object', + properties: { + name: {type: 'string'}, + type: {type: 'string'} + }, + required: ['name', 'type'] + } + } + }, + primaryType: {type: 'string'}, + domain: {type: 'object'}, + message: {type: 'object'} + } +} +``` + +##### Returns + +`DATA`: Signature. As in `eth_sign` it is a hex encoded 129 byte array starting with `0x`. It encodes the `r`, `s` and `v` parameters from appendix F of the [yellow paper][yellow] in big-endian format. Bytes 0...64 contain the `r` parameter, bytes 64...128 the `s` parameter and the last byte the `v` parameter. Note that the `v` parameter includes the chain id as specified in [EIP-155][eip-155]. + +[yellow]: https://ethereum.github.io/yellowpaper/paper.pdf +[eip-155]: https://eips.ethereum.org/EIPS/eip-155 + +##### Example + +Request: +```shell +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_signTypedData","params":["0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", {"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":1,"verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","wallet":"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"},"contents":"Hello, Bob!"}}],"id":1}' +``` + +Result: +```JavaScript +{ + "id":1, + "jsonrpc": "2.0", + "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" +} +``` + +An example how to use solidity ecrecover to verify the signature calculated with `eth_signTypedData` can be found in the EIP712 [Example.js][example-js]. The contract is deployed on the testnet Ropsten and Rinkeby. + +[example-js]: https://github.com/ethereum/EIPs/blob/master/assets/eip-712/Example.js + +#### personal_signTypedData + +There also should be a corresponding `personal_signTypedData` method which accepts the password for an account as the last argument. + +### Specification of the Web3 API + +Two methods are added to [Web 3 version 1][web3-1] that parallel the `web3.eth.sign` and `web3.eth.personal.sign` methods. + +[web3-1]: http://web3js.readthedocs.io/en/1.0/index.html + +#### web3.eth.signTypedData + +```JavaScript +web3.eth.signTypedData(typedData, address [, callback]) +``` + +Signs typed data using a specific account. This account needs to be unlocked. + +##### Parameters + +1. ``Object`` - Domain separator and typed data to sign. Structured according to the JSON-Schema specified above in the `eth_signTypedData` JSON RPC call. +2. ``String|Number`` - Address to sign data with. Or an address or index of a local wallet in :ref:`web3.eth.accounts.wallet `. +3. ``Function`` - (optional) Optional callback, returns an error object as first parameter and the result as second. + +---- +**Note** The 2. ``address`` parameter can also be an address or index from the `web3.eth.accounts.wallet `. It will then sign locally using the private key of this account. +---- + +##### Returns + +``Promise`` returns ``String`` - The signature as returned by `eth_signTypedData`. + +##### Example + +See the `eth_signTypedData` JSON-API example above for the value of `typedData`. + +```JavaScript +web3.eth.signTypedData(typedData, "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826") +.then(console.log); +> "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" +``` + +#### web3.eth.personal.signTypedData + +```JavaScript +web3.eth.personal.signTypedData(typedData, address, password [, callback]) +``` + +Identical to `web3.eth.signTypedData` except for an additional `password` parameter analogous to `web3.eth.personal.sign`. + +## Rationale + + + +The `encode` function is extended with a new case for the new types. The first byte of the encoding distinguishes the cases. For the same reason it is not safe to start immediately with the domain separator or a `typeHash`. While hard, it may be possible to construct a `typeHash` that also happens to be a prefix of a valid RLP encoded transaction. + +The domain separator prevents collision of otherwise identical structures. It is possible that two DApps come up with an identical structure like `Transfer(address from,address to,uint256 amount)` that should not be compatible. By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision. + +The domain separator also allows for multiple distinct signatures use-cases on the same struct instance within a given DApp. In the previous example, perhaps signatures from both `from` and `to` are required. By providing two distinct domain separators these signatures can be distinguished from each other. + +**Alternative 1**: Use the target contract address as domain separator. This solves the first problem, contracts coming up with identical types, but does not address second use-case. The standard does suggest implementors to use the target contract address where this is appropriate. + +The function `hashStruct` starts with a `typeHash` to separate types. By giving different types a different prefix the `encodeData` function only has to be injective for within a given type. It is okay for `encodeData(a)` to equal `encodeData(b)` as long as `typeOf(a)` is not `typeOf(b)`. + +### Rationale for `typeHash` + +The `typeHash` is designed to turn into a compile time constant in Solidity. For example: + +```Solidity +bytes32 constant MAIL_TYPEHASH = keccak256( + "Mail(address from,address to,string contents)"); +``` + +For the type hash several alternatives where considered and rejected for the reasons: + +**Alternative 2**: Use ABIv2 function signatures. `bytes4` is not enough to be collision resistant. Unlike function signatures, there is negligible runtime cost incurred by using longer hashes. + +**Alternative 3**: ABIv2 function signatures modified to be 256-bit. While this captures type info, it does not capture any of the semantics other than the function. This is already causing a practical collision between ERC20's and ERC721's `transfer(address,uint256)`, where in the former the `uint256` revers to an amount and the latter to a unique id. In general ABIv2 favors compatibility where a hashing standard should prefer incompatibility. + +**Alternative 4**: 256-bit ABIv2 signatures extended with parameter names and struct names. The `Mail` example from a above would be encoded as `Mail(Person(string name,address wallet) from,Person(string name,address wallet) to,string contents)`. This is longer than the proposed solution. And indeed, the length of the string can grow exponentially in the length of the input (consider `struct A{B a;B b;}; struct B {C a;C b;}; …`). It also does not allow a recursive struct type (consider `struct List {uint256 value; List next;}`). + +**Alternative 5**: Include natspec documentation. This would include even more semantic information in the schemaHash and further reduces chances of collision. It makes extending and amending documentation a breaking changes, which contradicts common assumptions. It also makes the schemaHash mechanism very verbose. + +### Rationale for `encodeData` + +The `encodeData` is designed to allow easy implementation of `hashStruct` in Solidity: + +```Solidity +function hashStruct(Mail memory mail) pure returns (bytes32 hash) { + return keccak256(abi.encode( + MAIL_TYPEHASH, + mail.from, + mail.to, + keccak256(mail.contents) + )); +} +``` + +it also allows for an efficient in-place implementation in EVM + +```Solidity +function hashStruct(Mail memory mail) pure returns (bytes32 hash) { + + // Compute sub-hashes + bytes32 typeHash = MAIL_TYPEHASH; + bytes32 contentsHash = keccak256(mail.contents); + + assembly { + // Back up select memory + let temp1 := mload(sub(order, 32)) + let temp2 := mload(add(order, 128)) + + // Write typeHash and sub-hashes + mstore(sub(mail, 32), typeHash) + mstore(add(order, 64), contentsHash) + + // Compute hash + hash := keccak256(sub(order, 32), 128) + + // Restore memory + mstore(sub(order, 32), temp1) + mstore(add(order, 64), temp2) + } +} +``` + +The in-place implementation makes strong but reasonable assumptions on the memory layout of structs in memory. Specifically it assume structs are not allocated below address 32, that members are stored in order, that all values are padded to 32-byte boundaries, and that dynamic and reference types are stored as a 32-byte pointers. + +**Alternative 6**: Tight packing. This is the default behaviour in Soldity when calling `keccak256` with multiple arguments. It minimizes the number of bytes to be hashed but requires complicated packing instructions in EVM to do so. It does not allow in-place computation. + +**Alternative 7**: ABIv2 encoding. Especially with the upcoming `abi.encode` it should be easy to use `abi.encode` as the `encodeData` function. The ABIv2 standard by itself fails the determinism security criteria. There are several valid ABIv2 encodings of the same data. ABIv2 does not allow in-place computation. + +**Alternative 8**: Leave `typeHash` out of `hashStruct` and instead combine it with the domain separator. This is more efficient, but then the semantics of the Solidity `keccak256` hash function are not injective. + +**Alternative 9**: Support cyclical data structures. The current standard is optimized for tree-like data structures and undefined for cyclical data structures. To support cyclical data a stack containing the path to the current node needs to be maintained and a stack offset substituted when a cycle is detected. This is prohibitively more complex to specify and implement. It also breaks composability where the hashes of the member values are used to construct the hash of the struct (the hash of the member values would dependent on the path). It is possible to extend the standard in a compatible way to define hashes of cyclical data. + +Similarly, a straightforward implementation is sub-optimal for directed acyclic graphs. A simple recursion through the members can visit the same node twice. Memoization can optimize this. + +## Rationale for `domainSeparator` + +Since different domains have different needs, an extensible scheme is used where the DApp specifies a `EIP712Domain` struct type and an instance `eip712Domain` which it passes to the user-agent. The user-agent can then apply different verification measures depending on the fields that are there. + +A field `string eip719dsl` can added and be rejected if the value does not match the hash of the [EIP-719][eip719] DSL interface string. + +[eip719]: https://github.com/ethereum/EIPs/issues/719 + +## Backwards Compatibility + + + +The RPC calls, web3 methods and `SomeStruct.typeHash` parameter are currently undefined. Defining them should not affect the behaviour of existing DApps. + +The Solidity expression `keccak256(someInstance)` for an instance `someInstance` of a struct type `SomeStruct` is valid syntax. It currently evaluates to the `keccak256` hash of the memory address of the instance. This behaviour should be considered dangerous. In some scenarios it will appear to work correctly but in others it will fail determinism and/or injectiveness. DApps that depend on the current behaviour should be considered dangerously broken. + +## Test Cases + + + +An example contract can be found in [Example.sol][ex-sol] and an example implementation of signing in Javascrtip in [Example.js][ex-js] + +[ex-sol]: https://github.com/ethereum/EIPs/blob/master/assets/eip-712/Example.sol +[ex-js]: https://github.com/ethereum/EIPs/blob/master/assets/eip-712/Example.js + +## Implementation + + + +To be done before this EIP can be considered accepted: + +* [x] Finalize specification of structure hashing +* [x] Domain separators +* [x] Add test vectors +* [ ] Review specification + +To be done before this EIP can be considered "Final": + +* [ ] Implement `eth_signTypedData` in major RPC providers. +* [ ] Implement `web3.eth.signTypedData` in Web3 providers. +* [ ] Implement `keccak256` struct hashing in Solidity. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/assets/eip-712/Example.js b/assets/eip-712/Example.js new file mode 100644 index 00000000..e4deebc8 --- /dev/null +++ b/assets/eip-712/Example.js @@ -0,0 +1,148 @@ +const ethUtil = require('ethereumjs-util'); +const abi = require('ethereumjs-abi'); +const chai = require('chai'); + +const typedData = { + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' } + ], + Mail: [ + { name: 'from', type: 'Person' }, + { name: 'to', type: 'Person' }, + { name: 'contents', type: 'string' } + ], + }, + primaryType: 'Mail', + domain: { + name: 'Ether Mail', + version: '1', + chainId: 1, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', + }, + message: { + from: { + name: 'Cow', + wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + }, + to: { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', + }, + contents: 'Hello, Bob!', + }, +}; + +const types = typedData.types; + +// Recursively finds all the dependencies of a type +function dependencies(primaryType, found = []) { + if (found.includes(primaryType)) { + return found; + } + if (types[primaryType] === undefined) { + return found; + } + found.push(primaryType); + for (let field of types[primaryType]) { + for (let dep of dependencies(field.type, found)) { + if (!found.includes(dep)) { + found.push(dep); + } + } + } + return found; +} + +function encodeType(primaryType) { + // Get dependencies primary first, then alphabetical + let deps = dependencies(primaryType); + deps = deps.filter(t => t != primaryType); + deps = [primaryType].concat(deps.sort()); + + // Format as a string with fields + let result = ''; + for (let type of deps) { + result += `${type}(${types[type].map(({ name, type }) => `${type} ${name}`).join(',')})`; + } + return result; +} + +function typeHash(primaryType) { + return ethUtil.sha3(encodeType(primaryType)); +} + +function encodeData(primaryType, data) { + let encTypes = []; + let encValues = []; + + // Add typehash + encTypes.push('bytes32'); + encValues.push(typeHash(primaryType)); + + // Add field contents + for (let field of types[primaryType]) { + let value = data[field.name]; + if (field.type == 'string' || field.type == 'bytes') { + encTypes.push('bytes32'); + value = ethUtil.sha3(value); + encValues.push(value); + } else if (types[field.type] !== undefined) { + encTypes.push('bytes32'); + value = ethUtil.sha3(encodeData(field.type, value)); + encValues.push(value); + } else if (field.type.lastIndexOf(']') === field.type.length - 1) { + throw 'TODO: Arrays currently unimplemented in encodeData'; + } else { + encTypes.push(field.type); + encValues.push(value); + } + } + + return abi.rawEncode(encTypes, encValues); +} + +function structHash(primaryType, data) { + return ethUtil.sha3(encodeData(primaryType, data)); +} + +function signHash() { + return ethUtil.sha3( + Buffer.concat([ + Buffer.from('1901', 'hex'), + structHash('EIP712Domain', typedData.domain), + structHash(typedData.primaryType, typedData.message), + ]), + ); +} + +const privateKey = ethUtil.sha3('cow'); +const address = ethUtil.privateToAddress(privateKey); +const sig = ethUtil.ecsign(signHash(), privateKey); + +const expect = chai.expect; +expect(encodeType('Mail')).to.equal('Mail(Person from,Person to,string contents)Person(string name,address wallet)'); +expect(ethUtil.bufferToHex(typeHash('Mail'))).to.equal( + '0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2', +); +expect(ethUtil.bufferToHex(encodeData(typedData.primaryType, typedData.message))).to.equal( + '0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8', +); +expect(ethUtil.bufferToHex(structHash(typedData.primaryType, typedData.message))).to.equal( + '0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e', +); +expect(ethUtil.bufferToHex(structHash('EIP712Domain', typedData.domain))).to.equal( + '0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f', +); +expect(ethUtil.bufferToHex(signHash())).to.equal('0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2'); +expect(ethUtil.bufferToHex(address)).to.equal('0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826'); +expect(sig.v).to.equal(28); +expect(ethUtil.bufferToHex(sig.r)).to.equal('0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d'); +expect(ethUtil.bufferToHex(sig.s)).to.equal('0x07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562'); diff --git a/assets/eip-712/Example.sol b/assets/eip-712/Example.sol new file mode 100644 index 00000000..dea246a2 --- /dev/null +++ b/assets/eip-712/Example.sol @@ -0,0 +1,106 @@ +pragma solidity ^0.4.24; + +contract Example { + + struct EIP712Domain { + string name; + string version; + uint256 chainId; + address verifyingContract; + } + + struct Person { + string name; + address wallet; + } + + struct Mail { + Person from; + Person to; + string contents; + } + + bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + + bytes32 constant PERSON_TYPEHASH = keccak256( + "Person(string name,address wallet)" + ); + + bytes32 constant MAIL_TYPEHASH = keccak256( + "Mail(Person from,Person to,string contents)Person(string name,address wallet)" + ); + + bytes32 DOMAIN_SEPARATOR; + + constructor () public { + DOMAIN_SEPARATOR = hash(EIP712Domain({ + name: "Ether Mail", + version: '1', + chainId: 1, + // verifyingContract: this + verifyingContract: 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC + })); + } + + function hash(EIP712Domain eip712Domain) internal pure returns (bytes32) { + return keccak256(abi.encode( + EIP712DOMAIN_TYPEHASH, + keccak256(bytes(eip712Domain.name)), + keccak256(bytes(eip712Domain.version)), + eip712Domain.chainId, + eip712Domain.verifyingContract + )); + } + + function hash(Person person) internal pure returns (bytes32) { + return keccak256(abi.encode( + PERSON_TYPEHASH, + keccak256(bytes(person.name)), + person.wallet + )); + } + + function hash(Mail mail) internal pure returns (bytes32) { + return keccak256(abi.encode( + MAIL_TYPEHASH, + hash(mail.from), + hash(mail.to), + keccak256(bytes(mail.contents)) + )); + } + + function verify(Mail mail, uint8 v, bytes32 r, bytes32 s) internal view returns (bool) { + // Note: we need to use `encodePacked` here instead of `encode`. + bytes32 digest = keccak256(abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + hash(mail) + )); + return ecrecover(digest, v, r, s) == mail.from.wallet; + } + + function test() public view returns (bool) { + // Example signed message + Mail memory mail = Mail({ + from: Person({ + name: "Cow", + wallet: 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826 + }), + to: Person({ + name: "Bob", + wallet: 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB + }), + contents: "Hello, Bob!" + }); + uint8 v = 28; + bytes32 r = 0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d; + bytes32 s = 0x07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562; + + assert(DOMAIN_SEPARATOR == 0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f); + assert(hash(mail) == 0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e); + assert(verify(mail, v, r, s)); + return true; + } +} diff --git a/assets/eip-712/eth_sign.png b/assets/eip-712/eth_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..d52d431201630516b4f42aa2b5367b6e679f8523 GIT binary patch literal 132226 zcmeFYWmp|e(=Lhz3lQ8P!JXjF!X4cj0r%ct&>TPd`vN zgwa1I`XdPS$1>73V=If+=9QC1R>A%d6BDhiq5g9MjfQ2Squn1ml8!lb+jxD(eLb1X zd%ym8Ha?bt2ayQ+isM`{}9C^^z#Yf&MLY10{G-~^?5(>1!K!WnO*M_kHg8W}a6d;yGJc2Q7i@$rOx zv7Y;AVBbJ)rrz|T(PDru6Vfd810M~c7XLTSqjqTr%*N;^xWQo%92Y5e1)e^HKNRB% z;G-c?INo*zmmasPK2l-l6miVvLD=sRhqAAnn#3dwpNu7fM|UcQHbbJfL41mWV zeqd%Y5hg=9tE;H;C)VSYU#K`K6)1mpWL(V?y{p_j9u;d@Y`GA7A(0-SDL?fkAvy|C zZn}M(L+#e9ix=$vAp8Txy<5Br?^g0hEc_amTl`_8aefC;%}_g*r#_Qzp^l~MvmAy_L+vdU`eqZ^ zB5Lq%`8HGSqPFaO)F$||iD5w5*~~dMj$qkkMoqkPSD6HupfY&z5peAIS@<&{L+tnu zpbx+Pi5c;v4d20ltrG#JKPyo0t4;WB05@#?pZ@)>(h*)7+3xXo?ESatQ!vr@4} zf)JQFAKgN*r=Z2V3HNs(G6LW(AR#|viUz9xfHCckH1j7M^5958P4YKXKwJon(MS3N z=^FI1`H2hhSO|j)iP4{K6TcbZtj}c{PB&2K0_7tays)r2Jkhl94I0L8LXq-OEbl2nd3M?z2WT|eZhL%%D!DC4k; zQRP&XFyV&|%AxAooX_ONa~gUY2BwC|=U|(|9+CyJ^W(!y_rJcW#fY9ojvlp~!Ztcgg!Mt{Z8=&2AZ zC@L_k7?jqiV3#SEWhl2)vJ`w%YgCI-9#_;U^c1}PdF;ggGp$t9A79NTb=kJj$+E*a z+E&zTn){~khX3aJM!+SnHPtiRv&ggb>B}w3tq?MMOa%TVz7rcQyPSnH>(@{kvf%h} z^**kV=KcpZIk${+uJz_$JC=hkF<#Z&y0a1q#Tt1Y>eog4R18I`{;Ej}Je8i6Vav_H z!aGdR((+2EE?fg0NNq70eztn_pY)tb4g6RO_jHmaEmFjkya z0WQC8Z3xO@s$~OYV`SqP`Z!jBL%{XBd6*r9VT=V?^Yn}PiDAYS+qg9!FLtm3m=r7y z?joco;zId_!bxZm8xR{sNIzkGg19J;|zvv>~$*)LH#}`W);_^}Jrcs)3|F{+i5!e3KRyF1BWA|N#2D1bd6NJviTIxsB=p~s--B+xGGFw|bSOyq-zjxbIzFjTS6 zuooE2kJ9uJCzMl8oGMIC7w{+)EEW7YPXJvsRJ1D28`lJzo$k1a<~NO^5^XVG3@|Q> zWDQ#+E)~z|^S(}n-f7w&u*1H?c5{;Z-h;*E{N*{uGsXydD+U!gqh$Nfg3RCYmXeed zgH-3T#A*0nU3TU=E#zh+i`UX@IeaE=q*)uxj3>F+l{_fD_G>YPzX^{7Bu<`6x?*`z zvy;169e)n%U;6Dae7j}4d3Vj{VbX49k!4|GF*(aWyI6d0=5Cf~_VGgFnc;acwI`LA zWy4I&6f}xIEH@;OAu=H~%uSZbnAGq>$6qQzeCYbDv;LkEP2j z;5<+VDL$kWNedf~K@(VF=Pip_NLLUUN0W>{!w za0%KY9(AkeR=ZGhu5i|F@z`5Lw;kU5-ZpbJGg;16&iH2i=F~q)Rz9ohR^gyq@5A6w zsSm1jSwFc;E=|Hpx~5*ZF{A~3UUR~D%C9;6(hyevA4yod}PA4p%ceQl>O z8Xf=EHPWZ$0<2nn@+EN7T6E0@HS#*UC7wMrBy?R*RORTns1&K}wMDccxdPTOx{5(K zi+W}Emgfv>V3TZpz5Cb0m&$p@`FS{Jl!{UztKaM|aK%LJu0wAMB9*eBs=nyHLWyp) z%XplwqG!(&^FPx`Mpc8Bap{B`o`Jr31t_KITAb?HiW_S`=B7MYZj7b#C3YeFN zji;17zU|DV%_h!{%Ee{S@UhNiEb`fNyja_fjwOK)&?nc-GiR0^1Wup(kpgfg8Xtj~ zEr%YpM?b-Z3CTK4i+T>7-xts3e7D_-H{_kU+~#y#T9)m{L7GMf1rxJ6>pEuDV7)GT zvfY7S>1dq1cKvIuPF~BPGu~C+$#c^UAD`;Y^^2E&RCv^H#AST+eg)Sh$0XQqT&ti} zpR&QO;unXp9dJj+da}Hzyudw!*EcWCSD81BTc@xC*_VKqMO_)6>3f#*y$iuhk}^MQ zACuSPhim~qASxxwT?ck2@uSr|K_w@6UCw)j1(W^eP-miT>z;z7LE!Z5@TnH0)LU%U zR}V-KQ1h;Rn0a77Jqe1>h>zz3{$S`p)UV04uz+}OM1{~qhq$r2c+DaA@{)p$@4zX9 zf2N}M%9iTK^Vj?%Rkd1S3K83FibyaH2QfU)y8xFH8*3!Xn8zjor$JBa-SbHxI=X!t z)_)GZ8)s{1=mHoY&*b%asSSc{@@K~*xp443IP(sDu5hCUz2$j*k*z}U{vgwfr`{+${Eg3q1j{nEz7 z+2FIgjkT>4k2^oqPeNPX5!6sEL!2qlLY*g`Mr^zxp*Wv~zLhCnf!h(SQH`Jx&vMi~r$d>-1l+-T^ZG zRl~&0_?79u`+jfA_g5*8f`z+@wYsQ z>HUxT-{<#hNNJ=xSJrnrK?o^PA!T>S(@X?!tohqPKPmVxKY|6-VMuvta{Ni7!r{MT z3iVs6G-yVtYBkOS85_^MDo-{F8C?n)vC8IE#l)Bc{Sm2?#BvOfgW-c~f(6lst^j81 z>Gx-=E!XSqyRbj)_w&cvQ`<7Y9SQfoP6w+_kW0b-pFTke3&O)ge)$aZ|N90xOt2SV z*V0l{)QYd7@~fnzLexTo8-<)A*Tt1%*$otWm&u!NVoLe*X97X&ykW#Yd4{tF97Ds^ zxGgeb+O0?15O5kU7R8wR=AbhuegCmqfrnnS;jJV~fpE~Vw@1C+fn@Xa3=hEP!v)2i z;+rWIaQ$=SQ||EA z3HUg*dm@uJUX&OQL0-?tWqe>8gv)b8e?!Dpl2!g|JLDgd65Ju{-saxQk}jUR!Q=k` z9s1G2o8eQ`KZ&}9NPk>xW5g{Iy3LrAkM4E_1#6d~{axJV(J<%&Z_n3hcc2wc@s)nl ze`2wpf+zQWxm{$pu&{uu6fTwg=RC2ALm@~fGgeB;$XG`MqsIR^kDEDYk`J%ob1QRR zbF6P-|E$bmMwe9Y^!Df|-A@}&SE#qdtMNrHi;D8^@PdxkY!o)cepI3=`8Txx>zJ!KJSlFc0L_)Tnjl97;?zlmqWS9`5frl-s2Q$g$ciGRvKQ(Ub zk)urpdMe$WNm3R=meY`HTDL+Q(E*1Up5~{#Y?+jei2t+THm5$7;B0AMi$GdBn&o*u z`b{(PBbSykK`3e0oVA*)1b!*d_XW7B+FI0>&dODhKL9uf#mHH z0dowS&$GZv7Ue8RK_>r7E3@1b#JW2x7o47e7ECu%T=RgMy4w76FQNfyY6t#A=gK60 z4J?(kNc|?A#VKO=Ly!HkJa3PL?(UbtTP(|s9Y3xSkBn&XT#(U5&k=d47(9Hyfbb>+ zgJQa9e5&Qnjhd25#K~M>@kt~u=6~O7+NYb zV~A4#h`lINu-Uxj9GZgSAwWDofif#5cI-p5!}6l_`J zcdu)`t{ap_HYqL}mT=q{hlA}G5Qp>@VoLOVs?3E4T^Vf6n@V73?dgJiceWlhVuQt1 zc`eUOU+xP+Ql@`|PlJ@I$wO=no^;j+` z+-BzFTj*J(R&!+avR)uNovK0Kex{^#R`_Y>VD3h|jbfb#0%fKL%{S^nc{^Y1rYz3Y zUrk>!&EEl&yBbD)GN*;vrp+if^9f$i^Nnp7lW}y(VUpK6^mtUDg2DQX)P3ccc(xp} zMwJo$+kvfB3sq}-AJ1It35{jBhECk6Um$KIa9;ze(Hrp1HOWw78AjQSdyPzFP0p|L z7J7^Z3}a!#GR4O}6o>2im_zjy0E~a#kRL5dKBMnBoKtde9ArH<4c`?gfl>EvcUf$7 zKRO{*#sqaURKH;q-<@yHhFXq!fpk|dmeGxKKHafe@x$c z4Ye{KC)2mL{G!(2NjxCm$&o@@!+V4}xpkS1Dw0ByO%j=^#~*V?12^azCL{o0()NJP&!gq#M1qL-mDu zoosStKW}Z~$b7AneF?{#Vu*5y!!y+^N{J&~o7@!#G;qg}5P*{ZAhQY->vv){n$$AZ z(;|=8LIOC~=mW>E%Z6B(%#E;Gx=ty#Ts)sVaMQ5#!0pG$*Dh4u!bc%`*#QTgG(f~r zd`Aj_hZQWz+BZg0<(}R>QglYEKnq{bYiTx1H|t=+SEgSHf*sfUVYN1H=v5v!Ot`Bd z>!15AR(`!v3}n5mC|JmMWJbmFvx;Ps5qXOk-l`?3?NDer{G#nZC{4I>{MqI|GeeCTv3MzP2wq+%?8&l%_Ye!DstD!xcPv z``4X>9|&lA5u|loTDK)qyz2)5lmprXyB4D2?HCzXoenQ~0gT>k2qp=gAgU2Ph!As1 zC|*HHF;;e^iR|c@;D^1aSzI)8)_N136dj3h6PePm0KUb3jEx~dhjl&rG{9O{A?2zx zE%Ie(Lpij}*N;5=fYG~bVrAiK+}-w8&m6_jiVZb(j#sokIR=r(>&62RvwFVPOrUo6 zh`|F4kMnV{&B6Q>4}nxzN}iF8h-fDFe4l##HHUv3IlOLfuQMFU)}1nuHmlcTv6UOw z>x`tL?(G~T&)jOJ{wajEVlRL(qbWC$1K zSeonPzRyLzGbT#@^vFWP=TFbC#}>GeNTeFiQQIp;+Zd8PW8SA4=TSErSoBY5DH$;x!GjW!g+`}$j1C`feD#L=Usha1T@1R9CPXl zhXo>=uNTRx|IlnK?NwIu10L@L-!X%Zx?XtQ47FU{$aOIvdfD-aM_YcgGvzqd|B|$j zM(U`+TX-{Ap;v8iCRn`nYK1<*H+>w$q$hfSyd|E{uvYOEWP}q988P$Hx ztZD*xYPnl%j;ZCjuKirt?_Jfy<$Nk0lWAz@;25yc%086! za$V+?ijs1=GvV~3q_Zg}lT8$*dU?d&SqB)p1wB0F7+JlbH^mp#a8+vu1`t5k{ z&bfiK>n1JjTiS|nLOM;8Pb_GriS}fD%^x(E-0qNV>~<21k{bUs^TvPF)Zq)-^J4M? zeXvGaCIGFkw0Tk8IVF<=bG*p0yf>_uYtXc!+;Su3ikm>;#Qd%KSh5v~vhQYvh;+_G zZ_86X>a=T*c7wG5HYZxN41WQ_G+g-C&|Vi7%If7>zQuJ~BB7 z0YvV-I&e?ZM(L6%%fJwEM;k4h{t?7P&UFn+nr!Y7*cY`{8&2S~XnPuu9OhUdLcleu zcjyAqAv>3kgKE7`du=Ll9`_Ibv6@6j_sIi{C4nc~wT?<$vIM{AS{B8rfqDIVpu*Ei zDF2@>e(JiRWl1vUXXw|n%!BPtR9D4su~MJ@$n>VpGQRGzYdc&kxv~ttUbSWQR8J7b zn)ZNwj{tUqiCrZ-e`0s1Ueuc^b%V8hoshBVfMHI?o9hR<14n(Hq4Lx5j2SD9RdXdFvZHOSHwS*}ShU~Bt zrWTG})~bEEb0=C|?<wwi2}ca);+X3rCp&b1HX~oOHIokQJ9W%1AaERAe(^^(InRgAOmDzb{>i4A z#H)+Ui|M|G^I%tRy2GtyXMN|@*ZSGKUC3wbnn1VN0isCeHRejqrilkSuImytW3Ia1 zNBHjDN+*Q+>)pV_7r|KA7C-e<{JZs*#W}+*PN7P{H9dtTr~5t+!Cmp$`jh(|J-SA*l*ThJFd!Se;MV`o=Kx@DPOESN!6Tt6q6)xsY(oIGRcEHYxMq1q)i88uQ7RYT2N<~1EZWfm$8q~ z@R`Ifsds!OGi*|zbC|1Fy_ODjXHZwa@Z={eJiCXqf~1G@)|g4x{gg_x*OMO^u=50u z)8J|CKnr847h`-(L)q_OjR0qRH(T36t|X(w=os!{xcdUNlQyGm*STlEeO&bztosI! z0d`Y%3`GU_T`e~I(t2^4=^sv!)tXP=JD*}stgbKGa>2#GuBI*hOZHsKavWcbG@`mlimOIi}B`BWWj+09r=`@87g?7Cikq^A|*J z@)zI*=1A^40o_qRQe5ZN-9kpfy;(|u)yg`)1|MN??OwDj-hRObj$qq5bdX4p#EQ7g*j6p$JsyfE1 z#>a$lBEzv>43t%RZXn#%ZFNW9s^;OG2J*dw!Vf`?HI(Z-gVgq2kTOq@t4=#oxm7!V zBkJ=Nb~W2dh_gdQP*LFmJk4CE+>PpKV7%6aYKx$W6*a>Y;qw=q;4SVXVSIDg zWAMwb%k0%)NM6&?eOiP*+-M~G{u8~%XZFp0k%_54xo3LCt7szU&b98eylOqFm@y-~ z3vR`64Vr1Ycqg`YRfhHsduOd{JtC~g&!VtVr1TOl!MuXLIDQ|amu$Q?+>}wNgJcG* z9Xna16QXhsK#N}Qp&6JSdWMdT_edbh_#S5&zkX2c4obC(&-`_EOeSY1!FlP0YEP`R zrusU9f2z%igTd4qknqJBHC`{&1ywOy6g_Xk3#WNM0LQ-PkC3^L-5k(HGs4TyMXZF$ zEBnn%20UvaS+e(+iitm6d$>)17@kMfvf1;q^>(K9bu{!Ft+W6aVKv`YNDC)u&JS^0 z`mLnOcRdMc`)$Py$I#-n0d2bVs#@4?fH^04O@7gCwBVU zbp(iEDt@79B&lk?QD#U~XJ2pG!P%8V$Hi0aD=3A!w>$R^y56QT+GxP>o4biL1*vU)hat=R28ayc2ts#J)u`96N&AwTWSG|P&J z3mh}T8rLrI1k~8SK8(}F2eO3K0Jh5B@`v;D83-Oo!ic`9w^vF7x`S!l)Q{d5dZ-`3 zt?>j|T=%RTGj-2_E{6Dtahd;9scmP`UHuX96?z`un!93gT)#i9|gl|Zr z)U$C8-OHBGJ5Q{a;VQ|jlyyosbBu&*$8p)cyIQ)rWf@Rv4B!YiGb8h3o(%hClO(B~ zVUtZrN4y%&;&A+sRk66OFg*FKJk8(oRTcETOkph~EVOjgV?_MkHDvHBPgh;peat#5 z%arbCeSt32>)Af2x_tK)KmL@*nD0m4fP1i|z;2cO+&6;#jNI+~Y#^h^RKibY2E4r) zOMr*IFyD}sAX^??JEpQSSH+6GS|PpDKj)B2Ymi7TTc@32?ZG9U=Vkfzt}S=I;7}~} zHXo9Z-qGLP0Uh2ky`F0c+C`pv%V&75I_>vn)sK}32Wk?9AUMv(F7DRo*ZYab42wK@ z!hOEF_wBs`0Opu6qhBRoj-RXw-=%kvoTVHPd1pS;Iq!6_g9P<@l>GYWqV1&VS6+E4 z@wojfAuy^C##e9{Y4MP%ES8efjuRVo!v(u6Xd2VnJ_3_y2jI3Au5D$^MBn=0$%bZ- z+yk!T=dEot=lynn+Sl1Zfhm=TNkKu8a=&nrV)V!)LCcGi-&#BserzG676JKiF`6t~ z4p|1OA+r-jx3ZR(Re^Oqr5>2bY^x0;B}hD7i&^V$a91=S*7bSJUNG-``q9Jcw9&Rh zxLWv7>`KCdf+Lsa-!ph4aS*F4chc3|zrv=t};?e%qo`wBb{6 zPoAu`aaYDDaVIL@*mVmh(^lz;e+!Dq6#th2MF{1BJu%P1T_eX&kpO)XTrFj+>WA4^ zy)XR>cYMb@@fOnfXWMiqgY@vce&oC@??Qd?!;o9m!~t9q&j_}xsJ+K8uc)uLxhofA z;DB1qbDrM7*x8g3RUb`IIO*2m7rrf8!V~VF>RNAW#V8v#Oz9cDsmtG0gW-xZqWoZA zGH$&GGrXB?yB^gJry2Q}nXJQD+2c3>@(GY2Ns5W`c)-y+@a?gdltBPZS|mSY%!|%O z%K>4<*9!fTBn*qWq*0ZX+*q0Pr-<2dEw1YNIBwa7XWuiOgs3phE??~xuPgmO<-o`_I?2B+7z zi*wd-wEh&M;h^Mmkmrryid?37;RM-5&+H_e4pK&7w=(d!ZTxkCL}_fh;OFg^dl>eN z^kj9X*r5*}cS^|C>gw%9!>kKVbs{)}NcBuiI0Hf~gw`FspNcK z*bJz|_B-$^xkd1VdX9^94=?0yhg@#o4)ChMJ#zh>9mxrHOcHsUF;v`lD+1658g!DO z+`OYLKMV!+&adoH5_1p_KFYjYK!4;Ty|C9mrs9q3KxKtrw9$)7+L-Jpm)mX_yIIcZKx;vBx@O9F-W!p0N%{c~8{!bygcBLvHTHbqSXDSHt)eWTJ`77|O;) z_gt3xIV^u2%tO-^m_W%FAt|eMo3ypotS5|HbvB}wbMeeBH+NjvTiC6rA%c+C=l$YD zCWkBs+%+VPC{bWv4p@B2p`@Eg7dw5!<(ntKkdy0^$aWg)sd0B<#ND~kQ5`kp{56@~ zO1#*`G8msd%LN|xLA6i&8f3rboyBSPY7c;`@&tb@ufpGXyC8gQN3E|HUHwaDfth!$ zD<=nb3_m-tc&Vdzn+0))TbnHjM@;s~T>Z5%p~4^oT9M2G$)#-n zvuv?UJToUio(YDuD;TKrBwmfyE^Jy@*xXdvAq^%Z8i6Wt7B=WsmWi+~ySg?fS%G4> z{6~umG(NvuB2{sA`&ZrrPDF|_T+CrEZ5}23t;BaYC@W}pqq;&Eq)b3cQOAjo?NW-u zz6D|kTXecw1z@4^{7xVEY`4h8$D7dYv`vD@6^wsy&*) zhl$q5v@|`CS?56dK}Wf0JcPIbwk*8OXncHwP5-H%(f$=wL6PZ>Sr-?^i5cJb6R)39 zsv!2p#aUgG?IL1NMRpCJuMID^^RKET-gDl*GbKlR>hA{G6WOQtSV(>0CzVOQK(Zrt z8{u!COL)0WU@MC{d`g=7A|-J?N)H-rn3tDQ4^nDEkpA24=-vavZzYp`ZzDc45_UN6 zK)Zw!E!WLa8>0(c!m81*)|wjoZ?7ZU^6bnDnXy@vNmrvcYnIjvPD9?5=(yG=07sOy z5*q#}(z05}8j>gzR=+GCDB;W)1d;Dz!}d-NlDL&njpO$<26@+QE^{-e|p zjg0gf<5bm36DQ{Uuh%CM)156|j=A|i zH{lvLZoopac8=0hA<0h6KOi3#&HO~-IC{>_X`>|yH~bzrb738CzPe4HK>`PVcLSC7 z%I@lY*1+p7>u`Y1V>CYd$6=K0%ep+Ql&`Pb`h-`xzaK14hkQyG?#Wj$D?8TjRH=PO zx+iUYu@^HrS1%z3kft-lkftxT#ml-nmp44$G^CD502ZeWbvRJMIlc(L#klGt=8^Vu zZ^!O`tTRT1SwpFYC6f5fVOc8NUKZw<7wMP%sqesT81fBNv+|HqZjSC%~M@Hyv|`dBz3M3!q=G>65jV5DmKWH6(;T-YCw{q>P&)qWb6 zd^V=urtkZz?|9`GfG&8Rxp+qw8DW5|c#;1CLhf)D4aDOnA|6FvlHZi^dfY}b_&b-I z&*)Rjn>$LJEI3*oj?=F<6{Ea*1v<5lti7i<+As$8WKK_BP`}jDKY7Ua__chBxdS$m zlW|a|t8=ozO#>#`PGBon(YVoZlOkpD?X(M5$`WN zp8Q76?G4RNCl*E~kncS^KuFUOd4cgY_|zw~yoTeJ_&ZqZ9KhR=&E~4Pg!FFAb-`>? zW-N^zYmnsLCTEi|uwo$e#4C+@Yh@hU)WWh_9OC2CFswBqwaIhgMeI->((GJuJ6wNk zV0eMYvzZwZ!1n<5*L5sf)i!pRZ1^Ug()Y(UAe5Xrwq~&~W&<{!Aomk}0vkB+%(L)u zJ%F$crBGfiHpIvqdg9NnN&3#mrCcpLctyL8RRQP<{m4jWx)-D>8i<-P9J#T|OXG@q zjVn;SodaN%hVK5!KqT#wgLxxju^0CNn;~Uw_8VB`)0_9WN#Zt~nMS_8f!arK+2Z<< z5rLF~!|Fb7ShP?#O~~(875N!Mk_ot@3-sX$$4s&cT_DWFNrmcuXLwWvf@}O0a<|`p zdpOxFbP#lz`J6~F=sl=*5X}p?(Z8Ba&mOEiK#ovuc41h4$-$`!CbiVY*S44;Z+Cw@ z=k>cvn98H~^aWq<*m;L1JWU&_j$ng`Hw$!qdKO7R=)U0652d$n74)^-O4*^>QY&6ImZUiW^OnW9az^mBSUx<@m+O%N{?M)}J~ zl7Mo+ny@xQj^R`Dd0^yev<%se{&cvWz7ZZnF@$|;OgS8FlJ75QcOPq)13qA9NFR+!G z#c)Y8Kv4aCSq>HW{~4@}va>2=qwEGmR>w14>@>hfSa9paayXn*Byag80wJ29B3 zxAy1M&iF_>?FR^&HSuJ3+Q(Or2lU?V%o_5$nw+YhSTh}B@xx$kKj?bDL4Q0$N4Xq$ zfOpo|-g}}D+4bT~{mOxp*NR_iaCVCLu-=(P**;N- zVcG8ZZc<7&RSmqY`C_ckKP8Zmp1Yqy%HaAm7?F1Y?+bLy3hth27f)V|yv}`(4P>}- ztMdVmvcm^VYcT{_TM}ZNB0##$S0@X5EIB9q72p3n;EWkdtN4)(iV3_J^P_K3u16MZ z4ne(I@n9k0DY3jzZDK)N*#9kI!Sw~W!04j~iH-z-a`g?nv-vq)W`Zb4W`iI_{+^Ot znnxoHS61Vu2OH^146#fYbv(g_xc>j1w6M-J{sVVcP4>AAMLEw<5AW`rTI6jZh`Fdw z)Hx4V<-A4l@_|ih@Rs&y0iy#fP;9Gnct&sqKbmRh|{JZl%`38 zb9m+<1=|>=D&W_t$oFrEkKSrI8kTeo+f8=9K}M|v0(38-7r44WV9>72QySg@0tT_5KA#UNs6 zwFqLwsZ1N;Ja5E>Hy|Ys9Sbt|HqY*DMSH7EBAafe5a({jW=i!3zA8Vo!+UgZLZ>CB%pl~7}R!eAnm z|CS6CAR?Nh8*=@{GC`T{pD9Aya)R$(kN9r^|462hjrWH?DEd=AL;3gYoq%SHPg{g; z1>bZ2p6w*4ME<$Ah32lTLF(_hJfF{CM7aiuN-?Sb&TtA7A>7_JvV+zJ9qMl!!+9am zCLAPaVgHsxBM8qX21S~YZdm;-{U2#u|6fBE6xPttkX5oy`R70c;eRK*qj8@}touhd zY4qA^ks)Q%b*!DFLKuk@64deD7#tsbp!S|BgIo82!`M zs&ShtKIPw`f0BPkvm>Q6@(<+Zb?;*0T&nsN`tCLP{NMEdcgX*glph zuRp>}-d4hOOyEe9GgAU9PKj}yl~ErarF6Tfams-S9nbkgTEetXal}Ek3{WKbZ(5Nd z+2dlDz7x`Kix3(<;s)w=9VVKshp=Hv3aP0bp$4LIs)yP~p&==4bzIhNj@aTa?kQ2l zbUGz4FQ3sO#((~m<@sF`D4Gt;oGy2BH4fc7i3dN@D*rh>B^_R$lMDK~pE7ZWM*IniQPo^~FB+)t8@y_krV_g! z38N=sJ4^aDS~an5oltI_s`Zzux=cW?%Js*Cx3@1xb)}SOL~zkPo7RAhPDnbUd(-wO z1-L1HfCTzDa0ywO?>)+}#@x>Km9iaKyfMI5mX?qi?7wT^KaLR9VU z=($2OZ=}9z{RJ~ZkZn+Cj$g5)3G@lhL~og4ehP)M6XP4c0P559hY?_H9^nH9j(SOXiW15hEgJF)8p zCXsw0k70`qr-GR;hDxc#r|Xs;igLzWvgFB4_w}E{aepg_wneL?$Xm+9yf2Glu(czt zCm}nGPyTA0=Rjl(QaqSHLd53XNrkf$dN(!U>BqLL%XH7Zq@e5nc%76h`j~zJr1z%C zSJI2-DhiuyQp9@Luh+(e^*!$m`HbfyPaHsd%g8giH{4|Z5wL^uph8Uar{oDT$LUNKpCm8 zDo(JwS=%M6w3YtVI#AIHpTJ|=hm;TKAL-8c#%{6saB97JcT8h$<@0@2W|bz$?5Dk4 z4+U3kqZw4o=DU_RV^U~aG`v&uppIfI+x^AqE>NlD7tIrzjo(Kp{VhIg+GE5QC^tuxJ2GW(Mn2}M82B(XUA`o;@iA_ zC?tOOZQPX@cLduebA?qyBh=|7gIdEytcTmK7nqb1h2y zA%o|`k^r1|yLH@~j~v--NByoxEA2%$ zlSA!eJ6`<88bT+F zVql#6{*q{GGB_T>Q~%b>{qiJ-E5JdZant>9G=5S;MqL!<=LJc5Q1e60`hdk;I{Rm2 z^l1`xOEs<+RI&d_+<=iAawA~4GwI#vN$&ZMU?oB1J+Sz^UMkw$T|8R_G#;|_du z>l_94?Wi4A#u)MN@OBYlluzmUJL?dYPyDKXn@-W>Pc04hbw_%z#&->h><;vDL8SeD z?F~1~eyK~qz);kY-7rGy9D=fw;4$2i@kH7)$Rdbc#5*`_kuLD$qlZg#w(kV!aL~wJ zo^a%Eq%*&D(7A9V^(}{+w%LT~^0oCB+Yk&ZtTr5IJAcg1A&cyFKo;9d!RNAwqhFB% zG8V-jPWQwg>chltGp+Xp6~p~@@8UMwiR?*Ly&{SN4EW^x)x?6wxQNs;=e{U>Z1Jz4 zKM(dLf<7Z&7{1zTZhRsAIU=}nAh82P@|uaCz1u>Xpo}M>CfK8M&;wW32C>c-4EiG> z`u(T`zE5J?kQlDUHyJV4YQo){v(tVa<_?4IPsPx3|9|;mUK8JAixO(H- z;X0utS-T6F%;p!Lg8;_th^mYOWyoR^bwRfc089Fqo0gZeoA>#oze*8}!x+f5`>n>& zh%-_8?AatwNlW_?*#$pPH0?QMfs|fb2cS+2$g6Tjz#})6AT9q^eGT!=4J+4P=?fyT z`Qt}PXa2~uMPXqRZYXqOkT_tQPClWOrfI@JivV^u+4x2cR*ci>tSz+D-VvQXo?k>`n2SkNND`32Wn@e+u4p{jk9*`wf0RP58tvW3WpPo*?qwexBOvw zjY`gx3E~LGjZoO^>H@`sHR}9o?W<_An0C|?5(W{9WPHa*g^?Ubk~~KcX>%FuWJlx? z=TD+4^Do{iK=t?S(?Yn%V?hz{BqSkpU-VLmzGyLT14cJ$=5PuE-{iXt_9c~1M-I1EtmUWgWZW;G>zzN)Fwf%y) zV$83^1EaeGx(ce>DHjX?NpC7)BH0z)a6Z~d;|RQoeV|E+E>HLYYH1V!KVd0G&79}0 z(Tnb-ky`(N-H=4fkYXZJwoTe5R}`ggXhUKE5>A?!on>gLoh2cA2;dO7IO!vYI?FmN zE8;^b4T?rE`VfN5Oo&yj5!h&=fs1{G#zFpFZjUJ~1ZzdaIFW87HyfOhV5b!t*NGhK zO0yKdOe`XftVp~+A9d%zzTn46QVzUW`6=owVEPfk@4l}kb9tHI)s)48zR{J$|wpA>R7>>bb z{t`wCG~Qog^fnlr+K~?G!2nl1TZFB-usAA*^~qLM@vbjT#5flbKsnr3W`qM}^x1o> z*IBxaj8Gh3v1O6q`T)bzVo@&M6fDC#3F(a9$f{x!)ZP-vvv>-Lg{d&hikEX1@;lsM z;^S~z6-ZORx-k`C*W$42mRJhlnjo9@8{*$2qDgqUMuN%=8ZsaUUj*FV82p2; z-O(om2IA)#?*%w~00qXmS|{Ik?VLJI4!QKL6Aw}5)j7;45%0Lmce?JC4$s%B$NR04 za%(&|KOW+*aT0`f;dO05S9vYoaN2E)jF(rR%ZJG>zhLI!dynqz)fM_JK0Jcqf31b1 zJV)-Ma0ih5{$K39^;6tI^Ddeo!QDN9;2vyof;++8-7PqaySqEV-CcqPmxbW&?u*0S zyx;TQI`{qur|SH)wN<;bpY7@CdAjH6XKXz>W>^LrlmTwj7s_#QfNzm$S#lJ<9k<2`aHCJk9h*aKY2v-%mf9l!Qz z<{v)PH%<*7!Og0;7Q9NYR*wyLw{H@S>+BOBi6Fj)#k#zOo#-!1=H-qN$~h_n%Wi+L z3_bkV#6{XC6Ye?kW{*i|1*H3k8cUWe91}Kuize<66++|zg=uf0jlzG!lP&b67*W?q zg#>CNt3CEI8s0)|SYmRP*1?<5xxuRS;0En1O68y=N9iE!j_j>2!}_mRWMcLL%|24+ zL|Q@Vr#nL(uT%ZOni6ma0?b8{c+R@{vh`1G(>mJ-hEilwKiAz4aF}pYFc| z0@bFRR8gdxy45Fdd%x<$0cOvrTZ|;M6drsClUM4zbf3V&?zFJns!ZeQgWRiuQ2rJl>xdyIV{S&}G=tLo#T!fpTM$6eUah5FeLOhhfW1Mg*l)W zhK zeN3whQLpoXt>ew!SU4>f@9?Qb7A(k`vgheS%|Xx_bHIa#iZKb!unc2u|7sik;J!lW_{R!crt*CtCj&F0u9(a8$XF zd8@oP2d*7>@_*6(b5v_wVt2S!LG*}}xvNZL8Uzg@Ld@{v`NHziWiTr??gF1*{uejm z4*vm7>jkW*_0Zdp0JV-d%iOaPKax4u`_t>&Pga}t9349?qpg-lrcY(rG|MBb=N8oM z6EklDILpC1I42mOr_7GiAv-rY&yo*a^FgY2}WI2-4<;&yMe7 z3t2nWt&?_Z`n?X`4WbdX51bW-aYO>K-VO);im}lIt#Ni^FF$e5#yPyvRvA4Y=*#zQ zz9SEg1lE_X&ZlwmOgW)#(Hq7SH}iYPD8q0!siHJ%wyg<pIl|d zf(6=SZA~!c8h<6@-c8&E?#;mYfVjuFWM6J+vz+BgLSw8CjH{1Z#)GY2HNGRY?p{MN z9pl=F#N%r7{!f@<%tFChB5xVKc7dPH0bw1}Y^*Td@MNs>ZfGT%+to#bLvH1;_FaYuH~z{{CFutT{zvH= zCDLhz7;NbFR2~eLEIt8=JGv~C`xvkro(#o{w{QDX&|Ao`68vz2>8)8_FA44NuE36q z{g9Ehh@7@MBx$KI3^zATN)->gJH)agHf#1p9{iaz*T9V{3q%6d)ai1crUc&~p`i_} zsaxHAp?O&?#P*rY_-a8ZS3=o2<-)Ml_2pid&m-lDmy8Jnfl<4)XIsv$bdK}ziD6Um z@CWD;{i$qimQj>~Lo^^GVM=$h)-n0XUkl3=Ss$mnZg^{2yXP!TI#+714r>m7Trk@< zUTEL3!C~a@yNkUNx^p6pef~L$2sc=!lis)-3KneQn)6g1Wv6 zqtMMBm%qCJ5$kC2WDGmANN`1Q?%`%Diy;&_IH^$^;C>M_;)^iZ-{5MWcU`{chP*Ts zvB3|V%-OM)ZU=@99KyslOT8*l9#-sBDCZ_z^L6TYd{Vy0?po-SEVL|HrHJ&-f`cdK zubXM_tPg^LGuM%WX%dO3E8VhNhm$8#ntjcoB6GnqTCaas+`->6c1stDyXhqAAmwag z@_k&tcySluC2x|n(dt*s_IW{ud@)Q!5{?rr7z!UtuYm+tM1q!y<_|wrY{KqOJI`N7 zRC12s#ui5X5gNaG3^P0xAdRc-Yb9b@z;_ix6;V>sESbls8DL(Vhs$rpD;V=_B*oy% zU@axcy&Zf#v=uxI18zr;Ea2d^@W7k1PYDfIs#vGHbU2x5ifxTbyPkt^d%4Dk%PNzzsCU6DMwXzUpAnas+r{b+Kqd0B@oCxULGOz4b7 zzm&2^bu+h5ZC`NfY%ueq;C0604(3P0t?@b&xx&fpSfb!v9Ow9&t~zBAC$ST(_mB?d zc8^ld4p4>p&yG23pV%(ybR~oib;JGClWe0zaz0rW(Vw(>QjtS84&8{c>n^5)ya zdt&tD;MnROf(RZFB*y5l_UVM&7TNQQf~U8)pRfE!8zQ8UCS{R@hp%}XnROxpjojnJ zRHy&d0x;vR$0l2Utt!DFE=d=eT$*9y!}8R*q|CYCL*22%KU56J!i9nhg&fzQn?E_Z zJ_0~!AoqJTWtzwf=zePD@Ik%!IB(~xn*BFEbX(&^P(xog~uL#Q^oz=M~NS4_^>K8{7 zb_(uy3&cf~{{6S8;pjqjZxF2=`$8)+`7{QO#N(S`D~Ai&G)v z>kcRs@J-+#t?m)j67VH6%1(kL@=f>7k%zKE2Ii1_Zb!9B35M*rU`RjLBKYCua6fgu`A=;cl`6uv54sf)yF@tCa_&5l`H#h!Lo@C`z_?f4o-Dz2XCj$w14{R zJDX@6Inq+rifIV4lZ_A$c@5LS*?Br(H^~{&p*Q!i{+5kh`g3aG@E$~drM5S-ZSHMk z;?oO-g^fg+aX3JwzNpWIbl6DmQm>Hs7*4}6V?>Mu^W3wg<9w;xfYNu0&k!E>(N_=} zDsq9!$oBIzJ9jx}_2)tC3zvM{i+SuR6+98M@89=9(spf)37K!axvTB`-$o;URO(#P zXH6Wv(x%crYTdgvKOr0 z!uEh9LE^$0mGE?q}N+@LilOS?gW8oYLZ5@Eo?9i=tlwNAX>@H zjN}Dwr`onk`MujAhJtQO*W0sgrXj~F?4#$}-7zjs_%`di+$|JgK1`<6Uu#)|MaM(=+OQl;-Bn5Y<3T*DrF*tZ*S zo?>0$PIc?BpW{b4a#_XGZiknBi^XM2w`VlNtXjN1keH?OZ^~RGtFk$HQi}ksW~yVV z;A)4b#9yyUdn_um+;taPvZmw@5hrK%3^4-LwG6LAm|H(2K7({pmTj8q>NxM%r}@aU zNJvE3t^|vdAFYU~<0We}hkwL9!|zz-Bjdo1UCw8GBj&J5Yp$&Nf=#$5ZMlGF9O~J# zkBT!6x1wf%JBXqB$-ipM1tl1pbxsXR6=T;BciAqmR2|Rg9%rO)8K~ zAZ|7Y>zRkCI)e-A9c6z zsc|O@OsATdd!C;DMM4imb1T6v)pR*~)y5-~n72(dIWd5<_BLe>QIN&{r{tP}s``S07+dN{H#yPs5kN z)4TKZ4HY*&YYAmI_As_L%GRyTyV#`P$h*m@f*&4VK1rHhJ=(EG2PZObvrmWqIM^DV zBpucf8e@`F(59H+b~SHKzR_r#^*Hk82OBUN;A}OeXx?wZRubE!0Epd~0NQg0YyEP1 zS>AJrZ58JWOeO#d9ernrSFej|o}o*9qO+yi+FEe!;HlwUT!6Ud6v+*u(jT$C)-R33 z&2GG_U&iL%SR#d{J3m}t_Tyf^d^p(8*)hn9votNBA%hOd_ies!kwf5rVb)PY_ZBMQ zM(ZESuXz&NujIQdgJg{@UTQ!Hf_=;3>(;ly*4tnMyR2uP>)&}A^Hx{5+p!cZLFtEh z*iU>6-Bc)sm8dQHAVY=%h7V3Aa)*8Wm;_oGkDxQjVu3$Ec8Q#Az1wK#Znc}{p<}{P zKkfYR>Mc6+1Gdg~IE@ld)3Nw$s#6^_F9s;Ys zq%&5r!AZLn)Yg7@%sf(U<|OQ^#+Sd+iNrBDUmH#IAJ4k3hZlpv1KB~I7jIceRoaQK`&)5y?^DC*tT0#<$EzGm%0C^AdtC=>f45HV z-Sd+G(be+eT*`fkUPbMPN1K*M=k``>{+bO1#rf+#*T%hnK_X}btj8a>mXESV+v^Wq zM6<3G7VrY)WIx+uEt!T!6q&`)k^JdK$Cqs%4qZh4-WygS-t+Q5AS;J6SX)IhvXFt5 zA;zf0!V1zXt*U*sS}WSHCUPu5c3*U$RZVm>6-%`cm2)gMe6^1gdAV-wq46%ZIY!r} z&hts0AS^d#!g0`ly}^U~?XJ(M@~v^)lVz_FIC&C?fb}V#0%5mG=3sLq{zRB!<4Acn zK3*;S8=|7etDK0GbS9T7I^xO zhJyMFrK52gF}%StSajG6*dJEcfw&j-Y?v%j@=10YcrR1u4%V@8nJdEHd5)9F9-b|| z$;X|vx%~b3FIGLGPx3v~8m*W7S zzgsT1fLl$_#JY}Fa5@~Xx6@XByttt2)k0r~uL!`rU@1~h7j`wid&J^(e4Ywolbv(^ zlHRd8VVnVJDg$YSRk_I8#mBjy@>{1pj55}ih5gmyZ{LF&)Zx@{jJC2UV@4eHq~s1L zLU<{ByC`pP3#I`PHgs}=sR5+J(V`ou>v{`KSlB8GSBT>}X&0^t`7+V!fW)g1%7XNA z)@45#VoxOVn3ETrDymlc($r4z%(iN+dj#{C`Zk}#6B$4v2_<%jb@vx!b@3WQ{B$h$ zk_&Jh$1{zDUCM6>Euyt*4p7?p4PvF%1I}N(1ttG-BxC&Jc`_byHUu4xwxl8#k^n`C zqga2NXYYDlqL25=2qpPkhCqp^@3DUszqP)Zb#^na?zNt;m6A8T^#~fbjM$|PME5Ytz3Px6}vRD`VGe^oI`F8UYnx*#DMi-+swb@8&>Z)-m&?W*_cNb)M$8CWD*+ z=(@xHo<HIEdVPtb zPtCrLcbFKj3fbSItD>V#@e^tfGYy9rB&uxH6e|ZiGuYCW6-5(niN7iaIS-syh`-gt zY8XBU*( zfDtMUSgC0}Yra~)?KhJy*=G$$>9!lzMJZa7QRL`$5TADt7lkiECXExUCK;y~@lvT~ z0v0Tk`ntwWK9LPpM(b8|X9eGNpw!$dbIohmLRE*GkaK6533 zz{a0buW_LlYL#MT{lEdHiB1KZB}myE4dJzGAIO1Z-LxR5C$PPbdDak8N{lV;wD^FL z-+r3Z$-LAGvr$PSDi*}0VdW_xKQcZPhXFlaH+eTu-ILyqaA4s(rUL}}`2O90rVI+g zeMuKXdTV{-5gz*;s>E3aEx4Udw7MzAMnoV7P*0$j$n>L^XrNuYEGZc(U_5AOeueN* zxLC%nJ*)oNP&%zkVl65hQ7JoBNOxA*O>1+FZ(MjK${w?%N8m@5ixf|jP+k+* z(%cpPHr^jRP}n|gxqyZ{SVkh|XmwX6%Ot1}`!hcv5RP8*a_=NyDngr|oB>_u%M*Dt z;Dp27cd{g{hCV5+=9tcSz3oM}thf{}*GD_AlU7pfU3oZ(XdH!6FHMa5!{w9aRpsJKRQ-d(-8p2F zds4sdC4y1d{_;dx|Gnj|4HDX=rv88nGvg&(uYpKraiaMep|{bCy6)OP&mAGsQc_a| z7a89VF}9*p%n*UU;5pf71EROfj3U50ljiR5;KvqXnhHq9ZvFgf)G~FeM)mi?prdlA zF_tQaqN(V66#L32ajZ%0p5i!1|604&Dza)o1BbNx^=JEujGf;1?qS|kSSVN+sw354 zi7e={I37wYXln3p+7jaE=24j5TKHx$Hx`i~U4Q~WYbYdkeK7F=E)XY36uc zv*mU^t}4ZTGrKxWt1l5n+QuPMxI;|c7#%nzFGvl32QL@AXe2#`Y|`|%NH+>r`TF=; z>6C%CuwFSX-suA+b|9e0)JkVGW{q&<3rvuZ3Di8zn%tP(MFo5;6V=Ryof__}H(#wN zQ-@E@r*3O$Ng>d^#`NFaeak`#Tr@M|(J_#s{?=jv z7Gj&|sb085L(hWIo)#7&{-CYVhV|J<_7-q0L^4WtZujv3HJv zTl9t}gm8d>wA~B5gx14X;P zMjn5rr$&ff4H2VdvFtCx$Fdd1+Ecq(3;?J&USX~-1MB+NqHBU@=L;CEfIha!OCT6j z2tCtZK8fnTa)K5XFy!F}pXf6PjM%qi^Qp}RAL!OQn1wo*l2Frr&d(f*6u6(<)3d(b zddrl?XghtRnze8?bqKeTU)$@>0XPb~MXYqf<*qlu({s;$`sR2x*)Wssovo^v;i-*U zoV9@4V5bu$Fs0+?NOBi%J9#pWg*o6a^S7t?hx^vgx+bxjsha2r?&!B4LC%F2!j}J) z@beFm>iwES)M#NAwZ~qtD!@`u8RG1WUTlFq6*D<#6-X46T|E^Aa+sIOuH7nZu`JO0 zJdg#HTPqRp<~#4H*T2MWQ~#Cd6`bK0J2jcFF-6fo!Sp9#n)~ zNjwp4sG=deLQuxgt5lHl&beS6r0f3UKgyv{AXQ$(!6)X7bQ9B$;oAy~imTwgnbtGD znq=sE18`>UPQN}*q0*7R3aS4aNCWx8=$RAR>+ejte+|g(&gyl+FvVks0YFw78jJJ|1as6T?49t6ajf6ro%tUAaMm;Xk#Bt8J1bpC~C#l zAS0Hyq)J1-5jskjA(WO7VJ1z(Ln34P_}1%!=;(IYtg$Ry@D{h|erb_r1!<%#AV2u; zu>Z?1#D#(yK8Xtam&^IT6ezJE6k{qnC;9)K9xMcJ0U=IU3+)#E?^I$xbQTDS@&69| z9}4vUHO2qyF#iXeqSsJ1ue21N%;(`tv&~WfMGOE6Psib-l9CdtG5PGz|B{qHLI{|9 z^5{@kSh;rv0aufQb#z0M8?NNV5raW6HKN%P?Camv?IFA?c~ zer6i*kF-tv`c)0lKMbZ5#{QRVp+xM^l<%9fiHS*2f=9A50MR_;dOMPt=OIXl0CnBx z*;W*DIKG-7B2oE&jqw3ev9u-Ad86Lx`qos2IBN zx$lUJgrnvIFB&`EOmKx>vlj(QQcSMO_Y_XfK=K*}j7iz=oc{^CJ5-Pc49ELi4c{~@ zmdCHd0l(C1^jc4gLKKQ(oI^oyf_;z26*WU?tj2tGm;Fc`ouVtE011k(3;z+3MahK>9I_!{ zl#hP-)d17ywBvNI{WtkV!<0b(T$%C~CA_9=g*K%0z~Z5CS zyV>i1xYp6aVKgByH#2JDhNY_!L&$-sgPJPn`{rG#-GZ9QWiOt}q&wYUu3%zrZrtnl z5mnph)nL0M_4heLtl(SgL1{zow^rFMpeO%}$KCP#W@y&lSgJO~8$^m$O?xS%8~FSL zZrShN{FX-0!@U_Tg5-L!zDCEa^%KYJZOv^%)6_vP^La*sEE?jZPur8gcB4T=n~m-@ zMB6{Ei)Z0Y7pL`{Hj94muNS_5`Foo$fz$aSa3`cQkIVOHD;G_JkuBPl*NZffg8ut_|L|QUbe`~K*w%q{>KA$$^!=c?}0LK}) zi#c1CtQ*LL_Q%tAk_ULEOl33JX?IubsQ1C%m%rf@DH<+CW`gnbEU>3nQ|{Nj&}M8l zzcF>)Dos7d*&2B{IXhpC($_3fwS$S7uCkmbe^<4vAGhw$n`hDJb&}wW!a;lx73tsN zi?mA-=TwEF+8Peno#ef^CH@jU)EDbu_MOdknLX8lD|sS70q?I*Py=nQ`eVF!ketV< zGnEgR!)2;D?f5b?ckCK-vU3r+=i_CE#~t%M!pg&0%f=Jqs3%rl`|-ooyc&c~kiXR* zs$KFDVaFjz=GBIDCDJ>_+;dvNU+gs~0~n5MSx0?jfC=i|`u?vMVrFV~luFu9BIYih zv!g7LTA^8oq*FZQ?B~;ZtV4_#%kkcgxnSQ?($vXn60I_Md!qXuTT>vlY_)F3-()d7 zBL?idK_dWG%LMzf73O5|lP(UC{NW2A{OBz3i}hg=DQnOMsl(#;9wKMYO@BDy5jlGl zVLS?AcYgTVJWj``A4xk2>-yfTIh`cQBmXfI(LXa~R;Pq;ib0)55?aonbWHGRI$Uo4 z{xdQX&VxxJ2k@F7F+6cMOoj+IYx}epkAyYzw_iq5wFCGi9v0hODoWC>3ELV^*w5sI zBVZTC)7f;l(jp95)SA!wZ{?5D@BHFTQl(Dm-ct@fu9@k)Zgyc~wk>OB{Ao+zOFgoQ zJujnyFpL!apPHKKd*u1fo8I>uzCHb>{q#A2jbR%In|&S=EGXg4 zONgG%C7r6U%_Cgr!zjFIg?>gTeW!}-Sc4OOwPgllPURB~;A z@^CU_745FsB~l_Iv?lXipM8Lb?@61%J^ z?6)av_C+SpS45YsKX;cj+>vs)zn_8>y7*nSRh}QxF1T=~sb;1Y+I0E8%Ij&9tO%{$360f%*~ar;YE38Auo0lz8mHK0(Bpm*ZKNU8$zQMWo^-uGT5hT^ ze|O?>+2O)Gwn~D?@Nst4{aCTHiuCCzHT@<7oFVP5UPrsyulmb84B1VH^&m)+Oz)Mk z%C-+?aU-2t`N)V_DN)tYummKQOQ$6Z6$HxlI&X7zU0B}@tx6doknPh{D8#jqCYwOz z=m8Q-0lcDjZ%S`6?Yob*E0-(ScAr$XZ0d7w8vfiZ13I(LZz^KyGlOb$aU%~McZS4n zJ-85@c?B;tSD#$&4P93+w3}_}`nJR}YfJ0d)|ihad#n-Ad|>i=5?v5la_LZ zB-Wek(cL#yqRto!1oKH_LKiTd%w8$mE?>C==lqGP-LD%uFXsH3g9F8zit6@J4#bK3 z2}ry+cM_brX@Eaqaq1620WoV*T%E#IODH;0&dHYo&5a7{=j*k|RKve%XhmxW2w7?vooVL{ibcEh`xMs!zQsv)D827mtKFM-qR9b;A-DmZvQZI~~ed3IXwa> zQ9biA_nMmg)a%O#buNJ1B5l<0$|gc?0yvHZFBdO%>vZaxd5*-vcpu$<#6)eikq+sT z59MuQ5a4V}FSR*NY_W^<#)U{bSVB5JR~w(zwcPO0H`Py1S6&)&U&rsBbnUyPl4|{w z`YVqL6mU3naz;}B;{NaiGL&{2kyI!W$O>!V2zWwl)Sm503W5Y#?q$jHGQI3C3ss2b ziLbp(VT)eC+BL&JR#^(AvU277>&k+;_4rG~Bh{xV4 zA;ly9dgQsbEOjcjH`2;voo!72;EKgE#By9`Z^?f-9HH7-OgEHciYh~c8(7ms^XRky zx?dIh7vu`P7;Nm?_HaWT4&oJXcZo;{Ju5QBzC!rGE~?ipr>&BgOY z&xHl_ia8sXqY5=sx$PCmtvHm;r{Q<^QaSoTJzz9^q(sYB8+fU<^7TLKR^Z=!3ji4z zZH-_wkke8$_DA%0hGQkBa`yT8nx^u0X{3lhFsw4F2Q zwu?sp%1gDxR4%`)J88cE=ZCJ#l3t0MZ1dKho10Eu$8!WuTB?8>b7dVv)OXh*0`s7! zDA3gq&~}*K?Bht`L%I9DfT0@!Zrlrczb!tCfzW5?OMOsj?XucDDJkt=*j6d;8tnS(sHJpu5~B}ciy*LJU)+$u6trrho$CM zs;#kwbXq~bE4q-=n@S|r%;Yk`(-+q;erBT6pXDd0xom^)`!_yZE0BBl_&iojNB{rN z_#t-vs($22v_d0e7)l}bcV(mi^LHz)paJ_e&vdG-A|$dV8)eTfS&ps+M}|I4GM5;Y z+{w6;gw<}zGK2;Qdc{IpFK*w>f~5VSXpq7{sDi$XOeT&0a>U1+PM0;81#Z1ZHgetDbkXSO zgu$<&%r}U>L))k8UKcKmQ*8MmD*`$Q3gs@QkcnBHtwN{5q`X5(l~oYsh7BwOT4i@y?O> z`8wdQHdY%;ai_~J;7xYZzX3saR(R%TF3+ctGouZlGOwEYId(`p`~L@1$@>9yxM9*$ z0m_+EM&JM>rpqjo;>d2vf{?Mjb}k484hVp%)bCj@j{yGAoJJ-FOk=7%S-b0ILH
  • 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 z

    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 LmY1pwu15a{DU{O` literal 0 HcmV?d00001 diff --git a/assets/eip-712/eth_signTypedData.png b/assets/eip-712/eth_signTypedData.png new file mode 100644 index 0000000000000000000000000000000000000000..38021297834d2cf5edbad8da1a86f617bc67f078 GIT binary patch literal 112451 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 Rn*#i4UDi|2zho2gKLAvB8T Date: Mon, 18 Jun 2018 12:17:30 +0100 Subject: [PATCH 011/177] Transient storage opcodes (#1153) * Create eip-transient_storage.md * Update eip-transient_storage.md * Update eip-transient_storage.md * Update eip-transient_storage.md * Update eip-transient_storage.md * Update eip-transient_storage.md * Update eip-transient_storage.md * Update and rename eip-transient_storage.md to eip-1153.md * Add missing colon --- EIPS/eip-1153.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 EIPS/eip-1153.md diff --git a/EIPS/eip-1153.md b/EIPS/eip-1153.md new file mode 100644 index 00000000..c2554a3a --- /dev/null +++ b/EIPS/eip-1153.md @@ -0,0 +1,75 @@ +--- +eip: 1153 +title: Transient storage opcodes +author: Alexey Akhunov (@AlexeyAkhunov) +discussions-to: https://ethereum-magicians.org/t/eip-transient-storage-opcodes/553 +status: Draft +type: Standards Track +category: Core +created: 2018-06-15 +--- + +## 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). + +## 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. + +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`. + +## Specification +Two new opcodes are added to EVM, `TLOAD` and `TSTORE`. + +They use the same arguments on stack as `SLOAD` and `SSTORE`. + +`TLOAD` pops one 32-byte word from the top of the stack, treats this value as the address, fetches 32-byte word from the transient storage at that address, and pops the value on top of the stack. + +`TSTORE` pops two 32-byte words from the top of the stack. The word on the top is the address, and the next is the value. TSTORE saves the value at the given address in the transient storage. + +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. + +The effects of 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". + +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). + +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. + +## 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. + +Relative pros of the transient storage: cheaper to use; does not change the semantics of the existing operations; very simple gas accounting rules; + +## Backwards Compatibility +This EIP requires a hard fork to implement. + +Since this EIP does not change semantics of any existing opcodes, it does not pose risk of backwards incompatibility for existing deployed contracts. + +## Test Cases +TBD + +## 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. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 3351cbf57f8189a9ec01ad7d8adb5b3b719bf645 Mon Sep 17 00:00:00 2001 From: ThunderDeliverer Date: Mon, 18 Jun 2018 17:41:46 +0200 Subject: [PATCH 012/177] First version of EIP-SDA. (#1129) * First version of EIP-SDA. * Added public discussion link. * Update and rename eip-SDA.md to eip-1129.md * Renamed eip-SDA.md to eip-1129.md * Removed eip-SDA.md. * Update eip-1129.md * Fixed some typos in EIP 1129. --- EIPS/eip-1129.md | 153 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 EIPS/eip-1129.md diff --git a/EIPS/eip-1129.md b/EIPS/eip-1129.md new file mode 100644 index 00000000..2097f647 --- /dev/null +++ b/EIPS/eip-1129.md @@ -0,0 +1,153 @@ +--- +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 +type: Standards Track +category: ERC +created: 2018-05-31 +--- + + + +## Simple Summary + +Standardisation of announcements in DAPPs and services on Ethereum network. This ERC provides proposed mechanics to increase the quality of service provided by DAPP developers and service providers, by setting a framework for announcements. Be it transitioning to a new smart contract or just freezing the service for some reason. + +## Abstract + +The proposed ERC defines format on how to post announcements about the service as well as how to remove them. It also defines mechanics on posting permissions and human friendly interface. + +## Motivation + +Currently there are no guidelines on how to notify the users of the service status in the DAPPs. This is especially obvious in ERC20 and it's derivates. If the service is impeded by any reason it is good practice to have some sort of guidelines on how to announce that to the user. The standardisation would also provide traceability of the service's status. + +## Specification + + +### Structures + +#### Announcer + +Stores information about the announcement maker. The `allowedToPost` stores posting permissions and is used for modifiers limiting announcement posting only to authorised entities. The `name` is used for human friendly identifier of the author to be stored. + +``` js +struct Announcer{ + bool allowedToPost; + string name; +} +``` + + +#### Announcement + +Stores information about the individual announcement. The human friendly author identifier is stored in `author`. Ethereum address associated with the author is stored in `authorAddress`. The announcement itself is stored in `post`. + +``` js +struct Announcement{ + string author; + address authorAddress; + string post; +} +``` + + + +### Methods +#### the number of ammouncements + +Returns the number of announcement currently active. + +OPTIONAL - this method can be used to provide quicker information for the UI, but could also be retrieved from `numberOfMessages` variable. + +``` js +function theNumberOfAnnouncements() public constant returns(uint256 _numberOfAnnouncements) +``` + + +#### read posts + +Returns the specified announcement as well as human friendly poster identificator (name or nickname). + +``` js +function readPosts(uint256 _postNumber) public constant returns(string _author, string _post) +``` + + +#### give posting permission + +Sets posting permissions of the address `_newAnnouncer` to `_postingPrivileges` and can also be used to revoke those permissions. The `_posterName` is human friendly author identificator used in the announcement data. + +``` js +function givePostingPermission(address _newAnnouncer, bool _postingPrivileges, string _posterName) public onlyOwner returns(bool success) +``` + + +#### can post + +Checks if the entity that wants to post an announcement has the posting privilieges. + +``` js +modifier canPost{ + require(posterData[msg.sender].allowedToPost); + _; +} +``` + + +#### post announcement + +Lets user post announcements, but only if they have their posting privileges set to `true`. The announcement is sent in `_message` variable. + +``` js +function postAnnouncement(string _message) public canPost +``` + + +#### remove announcement + +Removes an announcement with `_messageNumber` announcement identifier and rearranges the mapping so there are no empty slots. The `_removalReason` is used to update users if the issue that caused the announcement is resolved or what are the next steps from the service provider / DAPP development team. + +``` js +function removeAnnouncement(uint256 _messageNumber, string _removalReason) public +``` + + + +### Events + +#### New announcement + +MUST trigger when new announcement is created. + +Every time there is a new announcement it should be advertised in this event. It holds the information about author `author` and the announcement istelf `message`. + +``` js +event NewAnnouncement(string author, string message) +``` + + +#### Removed announcement + +MUST trigger when an announcement is removed. + +Every time an announcement is removed it should be advertised in this event. It holds the information about author `author`, the announcement itself `message`, the reason for removal or explanation of the solution `reason` and the address of the entity that removed the announcement `remover`. + +``` js +event RemovedAnnouncement(string author, string message, string reason, address remover); +``` + +## Rationale + +The proposed solution was designed with UX in mind . It provides mechanics that serve to present the announcements in the user friendly way. It is meant to be deployed as a Solidity smart contract on Ethereum network. + +## Test Cases + +The proposed version is deployed on Ropsten testnet all of the information can be found [here](https://ropsten.etherscan.io/address/0xb04f67172b9733837e59ebaf03d277279635c8e6#readContract). + +## Implementation + + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From b582c6579bc119560d30a8e15b0ede7f302e8c30 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 18 Jun 2018 11:42:44 -0400 Subject: [PATCH 013/177] Skinny CREATE2 (#1014) * Create Skinny_CREATE2.md * Update Skinny_CREATE2.md * Update and rename Skinny_CREATE2.md to eip-1014.md * Update eip-1014.md * Update eip-1014.md * Update eip-1014.md * Update eip-1014.md --- EIPS/eip-1014.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 EIPS/eip-1014.md diff --git a/EIPS/eip-1014.md b/EIPS/eip-1014.md new file mode 100644 index 00000000..da654104 --- /dev/null +++ b/EIPS/eip-1014.md @@ -0,0 +1,23 @@ +--- +eip: 1014 +title: Skinny CREATE2 +author: Vitalik Buterin (@vbuterin) +category: Core +type: Standards Track +status: Draft +created: 2018-04-20 +--- + +### Specification + +Adds a new opcode at 0xf5, which takes 4 stack arguments: endowment, memory_start, memory_length, salt. Behaves identically to CREATE, except using `sha3(msg.sender ++ salt ++ init_code)[12:]` instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. + +### Motivation + +Allows interactions to (actually or counterfactually in channels) be made with addresses that do not exist yet on-chain but can be relied on to only possibly eventually contain code that has been created by a particular piece of init code. Important for state-channel use cases that involve counterfactual interactions with contracts. + +#### Option 2 + +Use `sha3(0xff ++ msg.sender ++ salt ++ init_code)[12:]` + +Rationale: ensures that addresses created with this scheme cannot collide with addresses created using the traditional `sha3(rlp([sender, nonce]))` formula, as 0xff can only be a starting byte for RLP for data many petabytes long. From ac657ad49bf58617053c9639654a74b40577fe05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20G=C3=B3rski?= Date: Mon, 18 Jun 2018 21:22:59 +0200 Subject: [PATCH 014/177] Automatically merged updates to draft EIP(s) 721 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-721.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index cbe03f4c..e1d82450 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -156,6 +156,8 @@ A wallet/broker/auction application MUST implement the **wallet interface** if i ```solidity /// @dev Note: the ERC-165 identifier for this interface is 0xf0b9e5ba +/// @dev Note: the application will get the prior owner of the token +/// via _from parameter -- but it will NOT see who called safeTransferFrom. interface ERC721TokenReceiver { /// @notice Handle the receipt of an NFT /// @dev The ERC721 smart contract calls this function on the recipient @@ -295,6 +297,8 @@ Failed transactions will throw, a best practice identified in ERC-223, ERC-677, Creating of NFTs ("minting") and destruction NFTs ("burning") is not included in the specification. Your contract may implement these by other means. Please see the `event` documentation for your responsibilities when creating or destroying NFTs. +We considered adding an operator parameter to `onERC721Received`. This would allow you be to approved for a token and then send it to a wallet/broken/auction application, then that application could recognize you as the one that sent it. Instead, we opted to not add an operator parameter. If you want to take a token from somebody else and send it to these applications on your behalf then you should use two transactions. People writing wallet/broken/auction should recognize that the from address they receive is the previous token owner from the perspective of the ERC-721 contract. Those applications may have a different concept of ownership (beneficial ownership) that they need to consider. + *Alternatives considered: only allow two-step ERC-20 style transaction, require that transfer functions never throw, require all functions to return a boolean indicating the success of the operation.* **ERC-165 Interface** From cd57cb477fd1697787095a6a27aa9fd18947754a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20G=C3=B3rski?= Date: Mon, 18 Jun 2018 21:38:24 +0200 Subject: [PATCH 015/177] Automatically merged updates to draft EIP(s) 721 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-721.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index e1d82450..0dc7ea5d 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -165,12 +165,12 @@ interface ERC721TokenReceiver { /// transfer. Return of other than the magic value MUST result in the /// transaction being reverted. /// Note: the contract address is always the message sender. - /// @param _from The sending address + /// @param _from The address which previously owned the token. /// @param _tokenId The NFT identifier which is being transfered /// @param data Additional data with no specified format /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` /// unless throwing - function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4); + function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4); } ``` From 1c9668e28de633bbf946bcebf1f2780bea3a2332 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Tue, 19 Jun 2018 10:36:39 -0400 Subject: [PATCH 016/177] Automatically merged updates to draft EIP(s) 721 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-721.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index 0dc7ea5d..e021a211 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -112,7 +112,7 @@ interface ERC721 /* is ERC165 */ { /// @param _tokenId The NFT to transfer function transferFrom(address _from, address _to, uint256 _tokenId) external payable; - /// @notice Set or reaffirm the approved address for an NFT + /// @notice Change or reaffirm the approved address for an NFT. /// @dev The zero address indicates there is no approved address. /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized /// operator of the current owner. From d34382fbc2fe73cd1c493b585728bafa749a6b61 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Tue, 19 Jun 2018 12:15:32 -0400 Subject: [PATCH 017/177] Automatically merged updates to draft EIP(s) 721 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-721.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index e021a211..abec3258 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -45,7 +45,7 @@ pragma solidity ^0.4.20; /// @title ERC-721 Non-Fungible Token Standard /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -/// Note: the ERC-165 identifier for this interface is 0x80ac58cd +/// Note: the ERC-165 identifier for this interface is 0x80ac58cd. interface ERC721 /* is ERC165 */ { /// @dev This emits when ownership of any NFT changes by any mechanism. /// This event emits when NFTs are created (`from` == 0) and destroyed @@ -94,7 +94,7 @@ interface ERC721 /* is ERC165 */ { /// @notice Transfers the ownership of an NFT from one address to another address /// @dev This works identically to the other function with an extra data parameter, - /// except this function just sets data to "" + /// except this function just sets data to "". /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer @@ -112,24 +112,24 @@ interface ERC721 /* is ERC165 */ { /// @param _tokenId The NFT to transfer function transferFrom(address _from, address _to, uint256 _tokenId) external payable; - /// @notice Change or reaffirm the approved address for an NFT. + /// @notice Change or reaffirm the approved address for an NFT /// @dev The zero address indicates there is no approved address. - /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized + /// Throws unless `msg.sender` is the current NFT owner, or an authorized /// operator of the current owner. /// @param _approved The new approved NFT controller /// @param _tokenId The NFT to approve function approve(address _approved, uint256 _tokenId) external payable; /// @notice Enable or disable approval for a third party ("operator") to manage - /// all of `msg.sender`'s assets. + /// all of `msg.sender`'s assets /// @dev Emits the ApprovalForAll event. The contract MUST allow /// multiple operators per owner. - /// @param _operator Address to add to the set of authorized operators. + /// @param _operator Address to add to the set of authorized operators /// @param _approved True if the operator is approved, false to revoke approval function setApprovalForAll(address _operator, bool _approved) external; /// @notice Get the approved address for a single NFT - /// @dev Throws if `_tokenId` is not a valid NFT + /// @dev Throws if `_tokenId` is not a valid NFT. /// @param _tokenId The NFT to find the approved address for /// @return The approved address for this NFT, or the zero address if there is none function getApproved(uint256 _tokenId) external view returns (address); @@ -155,8 +155,8 @@ interface ERC165 { A wallet/broker/auction application MUST implement the **wallet interface** if it will accept safe transfers. ```solidity -/// @dev Note: the ERC-165 identifier for this interface is 0xf0b9e5ba -/// @dev Note: the application will get the prior owner of the token +/// @dev Note: the ERC-165 identifier for this interface is 0xf0b9e5ba. +/// Note: the application will get the prior owner of the token /// via _from parameter -- but it will NOT see who called safeTransferFrom. interface ERC721TokenReceiver { /// @notice Handle the receipt of an NFT @@ -165,7 +165,7 @@ interface ERC721TokenReceiver { /// transfer. Return of other than the magic value MUST result in the /// transaction being reverted. /// Note: the contract address is always the message sender. - /// @param _from The address which previously owned the token. + /// @param _from The address which previously owned the token /// @param _tokenId The NFT identifier which is being transfered /// @param data Additional data with no specified format /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` @@ -179,7 +179,7 @@ The **metadata extension** is OPTIONAL for ERC-721 smart contracts (see "caveats ```solidity /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -/// Note: the ERC-165 identifier for this interface is 0x5b5e139f +/// Note: the ERC-165 identifier for this interface is 0x5b5e139f. interface ERC721Metadata /* is ERC721 */ { /// @notice A descriptive name for a collection of NFTs in this contract function name() external view returns (string _name); @@ -223,7 +223,7 @@ The **enumeration extension** is OPTIONAL for ERC-721 smart contracts (see "cave ```solidity /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -/// Note: the ERC-165 identifier for this interface is 0x780e9d63 +/// Note: the ERC-165 identifier for this interface is 0x780e9d63. interface ERC721Enumerable /* is ERC721 */ { /// @notice Count NFTs tracked by this contract /// @return A count of valid NFTs tracked by this contract, where each one of From 27788131d5975daacbab607076f2ee04624f9dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20G=C3=B3rski?= Date: Tue, 19 Jun 2018 19:23:02 +0200 Subject: [PATCH 018/177] Automatically merged updates to draft EIP(s) 721 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-721.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index abec3258..96db407b 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -85,7 +85,7 @@ interface ERC721 /* is ERC165 */ { /// `_tokenId` is not a valid NFT. When transfer is complete, this function /// checks if `_to` is a smart contract (code size > 0). If so, it calls /// `onERC721Received` on `_to` and throws if the return value is not - /// `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`. + /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer @@ -155,9 +155,7 @@ interface ERC165 { A wallet/broker/auction application MUST implement the **wallet interface** if it will accept safe transfers. ```solidity -/// @dev Note: the ERC-165 identifier for this interface is 0xf0b9e5ba. -/// Note: the application will get the prior owner of the token -/// via _from parameter -- but it will NOT see who called safeTransferFrom. +/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. interface ERC721TokenReceiver { /// @notice Handle the receipt of an NFT /// @dev The ERC721 smart contract calls this function on the recipient @@ -165,12 +163,13 @@ interface ERC721TokenReceiver { /// transfer. Return of other than the magic value MUST result in the /// transaction being reverted. /// Note: the contract address is always the message sender. + /// @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 transfered - /// @param data Additional data with no specified format - /// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` + /// @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)"))` /// unless throwing - function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4); + function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4); } ``` @@ -297,7 +296,7 @@ Failed transactions will throw, a best practice identified in ERC-223, ERC-677, Creating of NFTs ("minting") and destruction NFTs ("burning") is not included in the specification. Your contract may implement these by other means. Please see the `event` documentation for your responsibilities when creating or destroying NFTs. -We considered adding an operator parameter to `onERC721Received`. This would allow you be to approved for a token and then send it to a wallet/broken/auction application, then that application could recognize you as the one that sent it. Instead, we opted to not add an operator parameter. If you want to take a token from somebody else and send it to these applications on your behalf then you should use two transactions. People writing wallet/broken/auction should recognize that the from address they receive is the previous token owner from the perspective of the ERC-721 contract. Those applications may have a different concept of ownership (beneficial ownership) that they need to consider. +We questioned if the `operator` parameter on `onERC721Received` was necessary. In all cases we could imagine, if the operator was important then that operator could transfer the token to themself and then send it -- then they would be the `from` address. This seems contrived because we consider the operator to be a temporary owner of the token (and transferring to themself is redundant). When the operator sends the token, it is the operator acting on their own accord, NOT the operator acting on behalf of the token holder. This is why the operator and the previous token owner are both significant to the token recipient. *Alternatives considered: only allow two-step ERC-20 style transaction, require that transfer functions never throw, require all functions to return a boolean indicating the success of the operation.* From b015a86658cfc12917507c067fff06f5fbec47fd Mon Sep 17 00:00:00 2001 From: William Entriken Date: Fri, 22 Jun 2018 00:07:34 -0400 Subject: [PATCH 019/177] Move EIP 721 to Final (#1170) --- EIPS/eip-721.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index 96db407b..423164ff 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -4,8 +4,7 @@ title: ERC-721 Non-Fungible Token Standard author: William Entriken , Dieter Shirley , Jacob Evans , Nastassia Sachs type: Standards Track category: ERC -status: Last Call -review-period-end: 2018-06-18 +status: Final created: 2018-01-24 requires: 165 --- From 6f068908257415a5ce36bd42a0a4bfd3d9dcfdea Mon Sep 17 00:00:00 2001 From: yarrumretep Date: Fri, 22 Jun 2018 08:10:35 -0400 Subject: [PATCH 020/177] Adding EIP 1167 - Minimal Proxy Contract (#1167) * adding EIP 1154 - Minimal Proxy Contract * Update eip-1154.md * fix email brackets * fix discussions-to email * Update eip-1154.md * renumber to avoid conflict --- EIPS/eip-1167.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 EIPS/eip-1167.md diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md new file mode 100644 index 00000000..9e1fa940 --- /dev/null +++ b/EIPS/eip-1167.md @@ -0,0 +1,53 @@ +--- +eip: 1167 +title: Minimal Proxy Contract +author: Peter Murray <@yarrumretep>, Nate Welsh <@flygoing>, Joe Messerman <@JAMesserman> +discussions-to: https://github.com/optionality/clone-factory/issues/10 +status: Draft +type: Standards Track +category: ERC +created: 2018-06-22 +--- + + + +## Simple Summary + +To simply and cheaply clone contract functionality in an immutable way, we propose to standardize on a minimal bytecode implementation which delegates all calls to a known, fixed address. +## Abstract + +By standardizing on a known minimal bytecode redirect implementation, this standard will allow 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. Specifically, tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run - and can depend on representations about that code (verified source, third-party audits, etc). We have an implementation we believe to be minimal and covering both standard calls and + + +## Motivation + +This standard is desireable to allow for use-cases wherein it is desireable to clone exact contract functionality with a minimum of side effects (e.g. memory slot stomping) and with super-cheap deployment of duplicate proxies. + +## Specification + +The exact bytecode of the standard clone contract is this: `6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd` wherein the bytes at idices 10 - 29 (inclusive) are replaced with the 20 byte address of the master functionality contract. The reference implementation of this is found at the [optionality/clone-factory](https://github.com/optionality/clone-factory) github repo. There are variations as well for using vanity contract addresses with leading zeros to further shrink the necessary clone bytecode (thus making it cheaper to deploy). Detection of clone and redirection is implemented in the clone-factory repo with a contract deployed on both Kovan and Mainnet that detects the presence of a clone and returns the destination address if the interrogated contract is a clone (handles shortened addresses as well). + +## Rationale + +The goals of this effort have been the following: +- inexpensive deployment (low gas to deploy clones) +- support clone initialization in creation transaction (through factory contract model) +- simple clone bytecode to encourage directly bytecode interrogation (see CloneProbe.sol in the clone-factory project) +- dependable, locked-down behavior - this is not designed to handle upgradability, nor should it as the representation we are seeking is stronger. +- small operational overhead - adds a single call cost to each call +- handles error return bubbling for revert messages + +## Backwards Compatibility + +There are no backwards compatibility issues. + +## Test Cases + +We have included some simple test cases in the clone-factory project that demonstrate the function of this contract including the error handling and error message propagation. + +## Implementation + +Please see [optionality/clone-factory](https://github.com/optionality/clone-factory) + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 8f73e7e436dba72a20855c1b6c7a903fa87dc7cf Mon Sep 17 00:00:00 2001 From: yarrumretep Date: Fri, 22 Jun 2018 08:42:33 -0400 Subject: [PATCH 021/177] Automatically merged updates to draft EIP(s) 1167 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1167.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index 9e1fa940..9e181d50 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -1,7 +1,7 @@ --- eip: 1167 title: Minimal Proxy Contract -author: Peter Murray <@yarrumretep>, Nate Welsh <@flygoing>, Joe Messerman <@JAMesserman> +author: Peter Murray (@yarrumretep), Nate Welsh (@flygoing), Joe Messerman (@JAMesserman) discussions-to: https://github.com/optionality/clone-factory/issues/10 status: Draft type: Standards Track From 972b21319946cf037e46e0650016e9513f3e864e Mon Sep 17 00:00:00 2001 From: William Entriken Date: Sun, 24 Jun 2018 14:57:24 -0400 Subject: [PATCH 022/177] Cache external link checks (#1164) * Cache external link checks * Cache the external checking results * Bump the build, add sushi * Bump build, remove sushi * Fix SSL errors --- .travis-ci.sh | 2 +- .travis.yml | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.travis-ci.sh b/.travis-ci.sh index 63f5d085..f907ac4d 100755 --- a/.travis-ci.sh +++ b/.travis-ci.sh @@ -1,7 +1,7 @@ #!/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 --url-ignore=/EIPS/eip-1,EIPS/eip-1,/EIPS/eip-107,/EIPS/eip-858" +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 diff --git a/.travis.yml b/.travis.yml index 92ea6370..295664f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,12 @@ sudo: false # route your build to the container-based infrastructure for a faste language: ruby -# Cache Ruby bundles -cache: bundler +cache: + # Cache Ruby bundles + - bundler + - directories: + - $TRAVIS_BUILD_DIR/tmp/.htmlproofer #https://github.com/gjtorikian/html-proofer/issues/381 + # Assume bundler is being used, therefore # the `install` step will run `bundle install` by default. @@ -31,3 +35,8 @@ notifications: 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 From 5ff1003117ce9fe9023f049774c086c82a57e45b Mon Sep 17 00:00:00 2001 From: achon22 Date: Sun, 24 Jun 2018 14:58:48 -0400 Subject: [PATCH 023/177] Multi-lcass Token Standard (#1178) * eip-1169 * added standard * Update and rename eip-1169.md to eip-1179.md * Update eip-1179.md * name change * Update eip-1178.md * Update eip-1178.md --- EIPS/eip-1178.md | 148 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 EIPS/eip-1178.md diff --git a/EIPS/eip-1178.md b/EIPS/eip-1178.md new file mode 100644 index 00000000..b609cc12 --- /dev/null +++ b/EIPS/eip-1178.md @@ -0,0 +1,148 @@ +--- +eip: 1178 +title: Multi-class Token Standard +author: Albert Chon +discussions-to: https://github.com/ethereum/EIPs/issues/1179 +status: Draft +type: Standards Track +category: ERC +created: 2018-06-22 +--- + + + +## Simple Summary + +A standard interface for multi-class fungible tokens. +## Abstract + +This standard allows for the implementation of a standard API for multi-class fungible tokens (henceforth referred to as "MCFTs") within smart contracts. This standard provides basic functionality to track and transfer ownership of MCFTs. +## Motivation + +Currently, there is no standard to support tokens that have multiple classes. In the real world, there are many situations in which defining distinct classes of the same token would be fitting (e.g. distinguishing between preferred/common/restricted shares of a company). Yet, such nuance cannot be supported in today's token standards. An ERC-20 token contract defines tokens that are all of one class while an ERC-721 token contract creates a class (defined by token_id) for each individual token. The ERC-1178 token standard proposes a new standard for creating multiple classes of tokens within one token contract. + +> Aside: In theory, while it is possible to implement tokens with classes using the properties of token structs in ERC-721 tokens, gas costs of implementing this in practice are prohibitive for any non-trivial application. + +## Specification +### ERC-20 Compatibility (partial) +**name** + +```solidity +function name() constant returns (string name) +``` + +*OPTIONAL - It is recommended that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.* + +Returns the name of the aggregate collection of MCFTs managed by this contract. - e.g. `"My Company Tokens"`. + +**class name** + +```solidity +function className(uint256 classId) constant returns (string name) +``` + +*OPTIONAL - It is recommended that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.* + +Returns the name of the class of MCFT managed by this contract. - e.g. `"My Company Preferred Shares Token"`. + +**symbol** +```solidity +function symbol() constant returns (string symbol) +``` + +*OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.* + +Returns a short string symbol referencing the entire collection of MCFT managed in this contract. e.g. "MUL". This symbol SHOULD be short (3-8 characters is recommended), with no whitespace characters or new-lines and SHOULD be limited to the uppercase latin alphabet (i.e. the 26 letters used in English). + +**totalSupply** +```solidity +function totalSupply() constant returns (uint256 totalSupply) +``` +Returns the total number of all MCFTs currently tracked by this contract. + +**individualSupply** +```solidity +function individualSupply(uint256 _classId) constant returns (uint256 individualSupply) +``` +Returns the total number of MCFTs of class `_classId` currently tracked by this contract. + +**balanceOf** +```solidity +function balanceOf(address _owner, uint256 _classId) constant returns (uint256 balance) +``` + +Returns the number of MCFTs of token class `_classId` assigned to address `_owner`. + +**classesOwned** +```solidity +function classesOwned(address _owner) constant returns (uint256[] classes) +``` + +Returns an array of `_classId`'s of MCFTs that address `_owner` owns in the contract. +> NOTE: returning an array is supported by `pragma experimental ABIEncoderV2` + +## Basic Ownership + +**approve** +```solidity +function approve(address _to, uint256 _classId, uint256 quantity) +``` +Grants approval for address `_to` to take possession `quantity` amount of the MCFT with ID `_classId`. This method MUST `throw` if `balanceOf(msg.sender, _classId) < quantity`, or if `_classId` does not represent an MCFT class currently tracked by this contract, or if `msg.sender == _to`. + +Only one address can "have approval" at any given time for a given address and `_classId`. Calling `approve` with a new address and `_classId` revokes approval for the previous address and `_classId`. Calling this method with 0 as the `_to` argument clears approval for any address and the specified `_classId`. + +Successful completion of this method MUST emit an `Approval` event (defined below) unless the caller is attempting to clear approval when there is no pending approval. In particular, an Approval event MUST be fired if the `_to` address is zero and there is some outstanding approval. Additionally, an Approval event MUST be fired if `_to` is already the currently approved address and this call otherwise has no effect. (i.e. An `approve()` call that "reaffirms" an existing approval MUST fire an event.) + + + +**transfer** +```solidity +function transfer(address _to, uint256 _classId, uint256 quantity) +``` +Assigns the ownership of `quantity` MCFT's with ID `_classId` to `_to` if and only if `quantity == balanceOf(msg.sender, _classId)`. A successful transfer MUST fire the `Transfer` event (defined below). + +This method MUST transfer ownership to `_to` or `throw`, no other outcomes can be possible. Reasons for failure include (but are not limited to): + +* `msg.sender` is not the owner of `quantity` amount of tokens of `_classId`'s. +* `_classId` does not represent an MCFT class currently tracked by this contract + +A conforming contract MUST allow the current owner to "transfer" a token to themselves, as a way of affirming ownership in the event stream. (i.e. it is valid for `_to == msg.sender` if `balanceOf(msg.sender, _classId) >= balance`.) This "no-op transfer" MUST be considered a successful transfer, and therefore MUST fire a `Transfer` event (with the same address for `_from` and `_to`). + +## Events +**Transfer** + +This event MUST trigger when MCFT ownership is transferred via any mechanism. + +Additionally, the creation of new MCFTs MUST trigger a Transfer event for each newly created MCFTs, with a `_from` address of 0 and a `_to` address matching the owner of the new MCFT (possibly the smart contract itself). The deletion (or burn) of any MCFT MUST trigger a Transfer event with a `_to` address of 0 and a `_from` address of the owner of the MCFT (now former owner!). + +NOTE: A Transfer event with `_from == _to` is valid. See the `transfer()` documentation for details. + +```solidity +event Transfer(address indexed _from, address indexed _to, uint256 _classId) +``` + +**Approval** +This event MUST trigger on any successful call to `approve(_to, _classId, quantity)` (unless the caller is attempting to clear approval when there is no pending approval). + +```solidity +event Approval(address indexed _owner, address indexed _approved, uint256 _classId) +``` +## Rationale + +### Current Limitations +The design of this project was motivated when I tried to create different classes of fungible ERC-721 tokens (an oxymoron) but ran into gas limits from having to create each tokens individually and maintain them in an efficient data structure for access. Using the maximum gas amount one can send with a transaction on Metamask (a popular web wallet), I was only able to create around 46 ERC-721 tokens before exhausting all gas. This experience motivated the creation of the multi-class fungible token standard. + + +## Backwards Compatibility + +Adoption of the MCFT standard proposal would not pose backwards compatibility issues as it defines a new standard for token creation. This standard follows the semantics of ERC-721 as closely as possible, but can't be entirely compatible with it due to the fundamental differences between multi-class fungible and non-fungible tokens. For example, the `ownerOf`, `takeOwnership`, and `tokenOfOwnerByIndex` methods in the ERC-721 token standard cannot be implemented in this standard. Furthermore, the function arguments to `balanceOf`, `approve`, and `transfer` differ as well. + +## Implementation + +Coming soon. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + From 33907618abf4bc2512b509a88c037ae36d20394c Mon Sep 17 00:00:00 2001 From: Lev Dubinets <3114081+ldub@users.noreply.github.com> Date: Sun, 24 Jun 2018 12:00:50 -0700 Subject: [PATCH 024/177] Fix typo in EIP-165, stadardizes->standardizes (#1168) --- EIPS/eip-165.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-165.md b/EIPS/eip-165.md index b6131ad8..fe22150f 100644 --- a/EIPS/eip-165.md +++ b/EIPS/eip-165.md @@ -23,7 +23,7 @@ Herein, we standardize the following: ## Motivation -For some "standard interfaces" like [the ERC-20 token interface](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, in order to adapt the way in which the contract is to be interacted with. Specifically for ERC-20, a version identifier has already been proposed. This proposal stadardizes the concept of interfaces and standardizes the identification (naming) of interfaces. +For some "standard interfaces" like [the ERC-20 token interface](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, in order to adapt the way in which the contract is to be interacted with. Specifically for ERC-20, a version identifier has already been proposed. This proposal standardizes the concept of interfaces and standardizes the identification (naming) of interfaces. ## Specification From 4636f54df7d480010accf8882a0fd0eef28a8742 Mon Sep 17 00:00:00 2001 From: Witek Date: Mon, 25 Jun 2018 00:29:35 -0700 Subject: [PATCH 025/177] eip-1155 Crypto Item Standard (#1181) * eip-1155 Crypto Item Standard * Update eip-1155.md --- EIPS/eip-1155.md | 174 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 EIPS/eip-1155.md diff --git a/EIPS/eip-1155.md b/EIPS/eip-1155.md new file mode 100644 index 00000000..bee5cffe --- /dev/null +++ b/EIPS/eip-1155.md @@ -0,0 +1,174 @@ +--- +eip: 1155 +title: Crypto Item Standard +author: Witek Radomski , Andrew Cooke +type: Standards Track +category: ERC +status: Draft +created: 2018-06-17 +discussions-to: https://github.com/ethereum/EIPs/issues/1155 +--- + +## Simple Summary + +A standard interface for multiple item/token definitions in a single deployed contract. + +## Abstract + +This standard proposes a new monolithic token contract that can mint any quantity of Fungible and Non-Fungible tokens in the same contract. We call these "Items" as they differ from the existing standards by being full definitions and configurations of multiple tokens inside a single contract. Standards like ERC-20 require deployment of separate contracts per token. The ERC-721 standard's Token ID is a single non-fungible index and the group of these non-fungibles is deployed as a single contract with settings for the entire collection. Instead, the Crypto Item Standard allows for each Item ID to represent a new configurable token type, which may have its own totalSupply value and other such attributes. + +The `_itemId` parameter is named as such and placed at the beginning of each function. + +## Motivation + +Tokens standards like ERC-20 and ERC-721 require a separate contract to be deployed for each fungible or NFT token/collection. This places a lot of redundant bytecode on the Ethereum blockchain and limits certain functionality by the nature of separating each token contract into its own permissioned address. With the rise of crypto games and platforms like [Enjin Coin](https://enjincoin.io/), game developers may be creating tens of thousands of items, and a new type of token standard is needed to support this. + +New functionality is possible with this design, such as transferring or approving multiple token types at once, saving on transaction costs. Trading (escrow / atomic swaps) of multiple tokens can be built on top of this standard and it removes the need to "approve" individual tokens separately. It is also easy to describe and mix multiple fungible or non-fungible tokens in a single contract. + +## Specification + +``` +solidity +interface ICryptoItems { + // Events + event Transfer(uint256 indexed _itemId, address indexed _from, address indexed _to, uint256 _value); + event Approval(uint256 indexed _itemId, address indexed _owner, address indexed _spender, uint256 _value); + + // Required Functions + function transfer(uint256[] _itemId, address[] _to, uint256[] _value) external returns (bool success); + function transferFrom(uint256[] _itemId, address[] _from, address[] _to, uint256[] _value) external returns (bool success); + function approve(uint256[] _itemId, address[] _spender, uint256[] _value) external returns (bool success); + function increaseApproval(uint256[] _itemId, address[] _spender, uint256[] _addedValue) external returns (bool success); + function decreaseApproval(uint256[] _itemId, address[] _spender, uint256[] _subtractedValue) external returns (bool success); + + // Required View Functions + function totalSupply(uint256 _itemId) external view returns (uint256); + function balanceOf(uint256 _itemId, address _owner) external view returns (uint256); + function allowance(uint256 _itemId, address _owner, address _spender) external view returns (uint256); + + // Optional View Functions + function name(uint256 _itemId) external view returns (string); + function symbol(uint256 _itemId) external view returns (string); + function decimals(uint256 _itemId) external view returns (uint8); + + // Optional Functions for Non-Fungible Items + function ownerOf(uint256 _itemId) external view returns (address); + function itemURI(uint256 _itemId) external view returns (string); + function itemByIndex(uint256 _itemId, uint256 _index) external view returns (uint256); + function itemOfOwnerByIndex(uint256 _itemId, address _owner, uint256 _index) external view returns (uint256); +} +``` + +### transfer + +Transfers quantities of each `_itemId[]` to the addresses specified. +Each parameter array should be the same length, with each index correlating. + +MUST trigger `Transfer` event. + +### transferFrom + +Transfers quantities of each `_itemId[]` from one or more `_from[]` addresses to the `_to[]` addresses specified. +Each parameter array should be the same length, with each index correlating. + +MUST trigger `Transfer` event. + +### approve + +Approves an account for the ability to transfer a maximum quantity of multiple `_itemId[]` on behalf of another account (using transferFrom). +Each parameter array should be the same length, with each index correlating. + +MUST trigger `Approval` event. + +### increaseApproval + +Increases the allowance amount of one or more items without requiring a reset to 0. +Each parameter array should be the same length, with each index correlating. + +MUST trigger `Approval` event. + +### decreaseApproval + +Decreases the allowance amount of one or more items without requiring a reset to 0. +Each parameter array should be the same length, with each index correlating. + +MUST trigger `Approval` event. + +### name + +Returns the Item ID's name. + +This function is OPTIONAL but highly recommended. + +### symbol + +Returns the Item ID's symbol. + +This function is OPTIONAL. + +### decimals + +Returns the Item ID's decimals. + +This function is OPTIONAL but highly recommended. + +### totalSupply + +Returns the Item ID's totalSupply. + +### balanceOf + +Returns an account's balance of specified Item ID. + +### allowance + +Returns the allowance set by any of the approve functions. + +### ownerOf + +For NFTs, this returns the owner of a specific `_itemId`. + +This function is OPTIONAL. + +### itemURI + +Returns a distinct Uniform Resource Identifier (URI) for a given `_itemId`. + +This function is OPTIONAL. + +### itemByIndex + +Enumerate valid NFTs. + +This function is OPTIONAL. + +### itemOfOwnerByIndex + +Enumerate NFTs assigned to an owner. + +This function is OPTIONAL. + +## Non-Fungible Items + +An example strategy to mix Fungible and Non-Fungible Items together in the same contract would be to pass the base item ID in the top 128 bits of the uint256 `_itemID` parameter and then use the bottom 128 bits for any extra data you wish to pass to the contract. + +Non-Fungible Items can be interacted with using an index based accessor into the contract/item data set. Therefore to access a particular item set within a mixed data contract and particular NFT within that set, `_itemID` could be passed as ``. + +Inside the contract code the two pieces of data needed to access the individual NFT can be extracted with uint128(~0) and the same mask shifted by 128. + +### Example of split ID bits + +``` +uint256 baseToken = 12345 << 128; +uint128 index = 50; + +balanceOf(baseToken, msg.sender); // Get balance of the base token +balanceOf(baseToken + index, msg.sender); // Get balance of the Non-Fungible token index +``` + +## Implementation + +- [Enjin Coin](https://enjincoin.io) ([github](https://github.com/enjin)) + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 3e9cbd47d214c8f826359d1813f00d08ce953dec Mon Sep 17 00:00:00 2001 From: Nitro888 Date: Mon, 25 Jun 2018 19:53:05 +0900 Subject: [PATCH 026/177] Wallets and Shops standard for safe pay (#1175) * Create eip-Wallet_&_shop_standard_for_tokens.md * Rename eip-Wallet_&_shop_standard_for_tokens.md to eip-WalletShopStandard4tokens.md * update pay, refund, prize function update pay, refund, prize function for safe and unsafe sop * Update eip-WalletShopStandard4tokens.md * Update eip-WalletShopStandard4tokens.md * Update and rename eip-WalletShopStandard4tokens.md to eip-1175.md * Update eip-1175.md --- eip-1175.md | 533 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 eip-1175.md diff --git a/eip-1175.md b/eip-1175.md new file mode 100644 index 00000000..fa8cc102 --- /dev/null +++ b/eip-1175.md @@ -0,0 +1,533 @@ +--- +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 +type: Standards Track +category: ERC +created: 2018-06-21 +requires: 20 +--- + +# All tokens go to heaven +## Simple Summary +Make wallets and shops created from certified contracts make erc20 tokens easy to use for commerce. + +![wallet](https://user-images.githubusercontent.com/11692220/41762799-ee17c480-7636-11e8-9930-681be2c59b56.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. + +## Motivation +New standards with improvements have been released, but the majority of tokens currently being developed are erc20 tokens. So I felt I needed a proposal to use old tokens in commerce. + To use various erc20 tokens for trading, you need a custom contract. However, a single wallet with a variety of tokens, and a mutually trusted store, can make transactions that are simple and efficient. The erc20 token is traded through two calls, `approve (address _spender, uint256 _value)` and `transferFrom (address _from, address _to, uint256 _value)`, but when using the wallet contract, `paySafe (address _shop, uint256 _item)`will be traded only in one call. +And if you only reuse the store interface, you can also trade using `payUnsafe (address _shop, uint256 _item)`. + +## Specification +![workflow](https://user-images.githubusercontent.com/11692220/41841025-2ed6e024-78a2-11e8-9faf-2b43aeaa2303.png) +## WalletCenter +### Methods +#### createWallet +Create wallet contract and add to list. Returns the address of new wallet. + +``` js +function createWallet() public returns (address _wallet) +``` + +#### isWallet +Returns true or false value for test this address is a created by createWallet. + +``` js +function isWallet(address _wallet) public constant returns (bool) +``` + +#### createShop +Create Shop contract and add to list. Returns the address of new Shop with erc20 token address. + +``` js +function createShop(address _erc20) public returns (address _shop) +``` + +#### isShop +Returns true or false value for test this address is a created by createWallet. + +``` js +function isShop(address _shop) public constant returns (bool) +``` + +### Events +#### Wallet +Search for my wallet. +``` js +event Wallet(address indexed _owner, address indexed _wallet) +``` + +#### Shop +Search for my shop. +``` js +event Shop(address indexed _owner, address indexed _shop, address indexed _erc20) +``` + +## Wallet +Wallet must be created by wallet center. +### Methods +#### balanceOf +Returns the account balance of Wallet. +``` js +function balanceOf(address _erc20) public constant returns (uint256 balance) +``` + +#### withdrawal +withdrawal `_value` amount of `_erc20` token to `_owner`. +``` js +function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) +``` + +#### paySafe +Pay for safe shop (created by contract) item with item index `_item`. +``` js +function paySafe(address _shop, uint256 _item) onlyOwner onlyShop(_shop) public payable returns (bool success) +``` + +#### payUnsafe +Pay for unsafe shop (did not created by contract) item with item index `_item`. +``` js +function payUnsafe(address _shop, uint256 _item) onlyOwner public payable returns (bool success) +``` + +#### payCancel +Cancel pay and refund. (only weekly model) +``` js +function payCancel(address _shop, uint256 _item) onlyOwner public returns (bool success) +``` + +#### refund +Refund from shop with item index `_item`. +``` js +function refund(uint256 _item, uint256 _value) public payable returns (bool success) +``` + +### Events +#### Pay +``` js +event Pay(address indexed _shop, uint256 indexed _item, uint256 indexed _value) +``` + +#### Refund +``` js +event Refund(address indexed _shop, uint256 indexed _item, uint256 indexed _value) +``` + +## Shop +Shop is created by wallet center or not. but Shop that created by wallet center is called safe shop. +### Methods +#### balanceOf +Returns the account balance of Shop. +``` js +function balanceOf(address _erc20) public constant returns (uint256 balance) +``` + +#### withdrawal +withdrawal `_value` amount of `_erc20` token to `_owner`. +``` js +function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) +``` + +#### pay +Pay from buyer with item index `_item`. +``` js +function pay(uint256 _item) onlyWallet(msg.sender) public payable returns (bool success) +``` + +#### refund +refund token to `_to`. +``` js +function refund(address _buyer, uint256 _item, uint256 _value) onlyWallet(_buyer) onlyOwner public payable returns (bool success) +``` + +#### resister +Listing item for sell. +``` js +function resister(uint8 _category, uint256 _price, uint256 _stock) onlyOwner public returns (uint256 _itemId) +``` + +#### update +Update item state for sell. (change item `_price` or add item `_stock`) +``` js +function update(uint256 _item, uint256 _price, uint256 _stock) onlyOwner public +``` + +#### price +Get token address and price from buyer with item index `_item`. +``` js +function price(uint256 _item) public constant returns (address _erc20, uint256 _value) +``` + +#### canBuy +`_who` can Buy `_item`. +``` js +function canBuy(address _who, uint256 _item) public constant returns (bool _canBuy) +``` + +#### isBuyer +`_who` is buyer of `_item`. +``` js +function isBuyer(address _who, uint256 _item) public constant returns (bool _buyer) +``` + +#### info +Set shop information bytes. +``` js +function info(bytes _msgPack) +``` + +#### upVote +Up vote for this shop. +``` js +function upVote() +``` + +#### dnVote +Down vote for this shop. +``` js +function dnVote() +``` + +#### about +Get shop token, up vote and down vote. +``` js +function about() view returns (address _erc20, uint256 _up, uint256 _down) +``` + +#### infoItem +Set item information bytes. +``` js +function infoItem(uint256 _item, bytes _msgPack) +``` + +#### upVoteItem +Up vote for this item. +``` js +function upVoteItem(uint256 _item) +``` + +#### dnVoteItem +Down vote for this item. +``` js +function dnVoteItem(uint256 _item) +``` + +#### aboutItem +Get Item price, up vote and down vote. +``` js +function aboutItem(uint256 _item) view returns (uint256 _price, uint256 _up, uint256 _down) +``` + +### Events +#### Pay +``` js +event Pay(address indexed _buyer, uint256 indexed _item, uint256 indexed _value) +``` + +#### Refund +``` js +event Refund(address indexed _to, uint256 indexed _item, uint256 indexed _value) +``` + +#### Item +``` js +event Item(uint256 indexed _item, uint256 _price) +``` + +#### Info +``` js +event Info(bytes _msgPack) +``` + +#### InfoItem +``` js +event InfoItem(uint256 indexed _item, bytes _msgPack) +``` + +## Implementation +Sample token contract address is [0x393dd70ce2ae7b30501aec94727968c517f90d52](https://ropsten.etherscan.io/address/0x393dd70ce2ae7b30501aec94727968c517f90d52) + +WalletCenter contract address is [0x1fe0862a4a8287d6c23904d61f02507b5044ea31](https://ropsten.etherscan.io/address/0x1fe0862a4a8287d6c23904d61f02507b5044ea31) + +WalletCenter create shop contract address is [0x59117730D02Ca3796121b7975796d479A5Fe54B0](https://ropsten.etherscan.io/address/0x59117730D02Ca3796121b7975796d479A5Fe54B0) + +WalletCenter create wallet contract address is [0x39da7111844df424e1d0a0226183533dd07bc5c6](https://ropsten.etherscan.io/address/0x39da7111844df424e1d0a0226183533dd07bc5c6) + + +## Appendix +``` js +pragma solidity ^0.4.24; + +contract ERC20Interface { + function totalSupply() public constant returns (uint); + function balanceOf(address tokenOwner) public constant returns (uint balance); + function allowance(address tokenOwner, address spender) public constant returns (uint remaining); + function transfer(address to, uint tokens) public returns (bool success); + function approve(address spender, uint tokens) public returns (bool success); + function transferFrom(address from, address to, uint tokens) public returns (bool success); + + event Transfer(address indexed from, address indexed to, uint tokens); + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); +} + +contract SafeMath { + function safeAdd(uint a, uint b) public pure returns (uint c) { + c = a + b; + require(c >= a); + } + function safeSub(uint a, uint b) public pure returns (uint c) { + require(b <= a); + c = a - b; + } + function safeMul(uint a, uint b) public pure returns (uint c) { + c = a * b; + require(a == 0 || c / a == b); + } + function safeDiv(uint a, uint b) public pure returns (uint c) { + require(b > 0); + c = a / b; + } +} + +contract _Base { + address internal owner; + address internal walletCenter; + + modifier onlyOwner { + require(owner == msg.sender); + _; + } + modifier onlyWallet(address _addr) { + require(WalletCenter(walletCenter).isWallet(_addr)); + _; + } + modifier onlyShop(address _addr) { + require(WalletCenter(walletCenter).isShop(_addr)); + _; + } + + function balanceOf(address _erc20) public constant returns (uint256 balance) { + if(_erc20==address(0)) + return address(this).balance; + return ERC20Interface(_erc20).balanceOf(this); + } + + function transfer(address _to, address _erc20, uint256 _value) internal returns (bool success) { + require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value); + if(_erc20==address(0)) + _to.transfer(_value); + else + ERC20Interface(_erc20).approve(_to,_value); + return true; + } + + function withdrawal(address _erc20, uint256 _value) public returns (bool success); + + event Pay(address indexed _who, uint256 indexed _item, uint256 indexed _value); + event Refund(address indexed _who, uint256 indexed _item, uint256 indexed _value); + event Prize(address indexed _who, uint256 indexed _item, uint256 indexed _value); +} + +contract _Wallet is _Base { + constructor(address _who) public { + owner = _who; + walletCenter = msg.sender; + } + + function pay(address _shop, uint256 _item) private { + require(_Shop(_shop).canBuy(this,_item)); + + address _erc20; + uint256 _value; + (_erc20,_value) = _Shop(_shop).price(_item); + + transfer(_shop,_erc20,_value); + _Shop(_shop).pay(_item); + emit Pay(_shop,_item,_value); + } + + function paySafe(address _shop, uint256 _item) onlyOwner onlyShop(_shop) public payable returns (bool success) { + pay(_shop,_item); + return true; + } + function payUnsafe(address _shop, uint256 _item) onlyOwner public payable returns (bool success) { + pay(_shop,_item); + return true; + } + function payCancel(address _shop, uint256 _item) onlyOwner public returns (bool success) { + _Shop(_shop).payCancel(_item); + return true; + } + + function refund(address _erc20, uint256 _item, uint256 _value) public payable returns (bool success) { + require((_erc20==address(0)?msg.value:ERC20Interface(_erc20).allowance(msg.sender,this))==_value); + if(_erc20!=address(0)) + ERC20Interface(_erc20).transferFrom(msg.sender,this,_value); + emit Refund(msg.sender,_item,_value); + return true; + } + function prize(address _erc20, uint256 _item, uint256 _value) public payable returns (bool success) { + require((_erc20==address(0)?msg.value:ERC20Interface(_erc20).allowance(msg.sender,this))==_value); + if(_erc20!=address(0)) + ERC20Interface(_erc20).transferFrom(msg.sender,this,_value); + emit Prize(msg.sender,_item,_value); + return true; + } + + function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) { + require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value); + if(_erc20==address(0)) + owner.transfer(_value); + else + ERC20Interface(_erc20).transfer(owner,_value); + return true; + } +} + +contract _Shop is _Base, SafeMath{ + address erc20; + constructor(address _who, address _erc20) public { + owner = _who; + walletCenter = msg.sender; + erc20 = _erc20; + } + + struct item { + uint8 category; // 0 = disable, 1 = non Stock, non Expire, 2 = can Expire (after 1 week), 3 = stackable + uint256 price; + uint256 stockCount; + + mapping(address=>uint256) customer; + } + + uint index; + mapping(uint256=>item) items; + + function pay(uint256 _item) onlyWallet(msg.sender) public payable returns (bool success) { + require(canBuy(msg.sender, _item)); + require((erc20==address(0)?msg.value:ERC20Interface(erc20).allowance(msg.sender,this))==items[_item].price); + + if(erc20!=address(0)) + ERC20Interface(erc20).transferFrom(msg.sender,this,items[_item].price); + + if(items[_item].category==1 || items[_item].category==2 && now > safeAdd(items[_item].customer[msg.sender], 1 weeks)) + items[_item].customer[msg.sender] = now; + else if(items[_item].category==2 && now < safeAdd(items[_item].customer[msg.sender], 1 weeks) ) + items[_item].customer[msg.sender] = safeAdd(items[_item].customer[msg.sender], 1 weeks); + else if(items[_item].category==3) { + items[_item].customer[msg.sender] = safeAdd(items[_item].customer[msg.sender],1); + items[_item].stockCount = safeSub(items[_item].stockCount,1); + } + + emit Pay(msg.sender,_item,items[_item].customer[msg.sender]); + return true; + } + + function payCancel(uint256 _item) onlyWallet(msg.sender) public returns (bool success) { + require (items[_item].category==2&&safeAdd(items[_item].customer[msg.sender],2 weeks)>now&&balanceOf(erc20)>=items[_item].price); + + items[_item].customer[msg.sender] = safeSub(items[_item].customer[msg.sender],1 weeks); + transfer(msg.sender, erc20, items[_item].price); + _Wallet(msg.sender).refund(erc20,_item,items[_item].price); + emit Refund(msg.sender,_item,items[_item].price); + + return true; + } + function refund(address _to, uint256 _item) onlyWallet(_to) onlyOwner public payable returns (bool success) { + require(isBuyer(_to,_item)&&items[_item].category>0&&(items[_item].customer[_to]>0||(items[_item].category==2&&safeAdd(items[_item].customer[_to],2 weeks)>now))); + require((erc20==address(0)?address(this).balance:ERC20Interface(erc20).balanceOf(this))>=items[_item].price); + + if(items[_item].category==1) + items[_item].customer[_to] = 0; + else if(items[_item].category==2) + items[_item].customer[_to] = safeSub(items[_item].customer[_to],1 weeks); + else + items[_item].customer[_to] = safeSub(items[_item].customer[_to],1); + + transfer(_to, erc20, items[_item].price); + _Wallet(_to).refund(erc20,_item,items[_item].price); + emit Refund(_to,_item,items[_item].price); + + return true; + } + + event Item(uint256 indexed _item, uint256 _price); + function resister(uint8 _category, uint256 _price, uint256 _stock) onlyOwner public returns (uint256 _itemId) { + require(_category>0&&_category<4); + require(_price>0); + items[index] = item(_category,_price,_stock); + index = safeAdd(index,1); + emit Item(index,_price); + return safeSub(index,1); + } + function update(uint256 _item, uint256 _price, uint256 _stock) onlyOwner public { + require(items[_item].category>0); + require(_price>0); + uint256 temp = items[_item].price; + items[_item].price = _price; + items[_item].stockCount = safeAdd(items[_item].stockCount,_stock); + + if(temp!=items[_item].price) + emit Item(index,items[_item].price); + } + + function price(uint256 _item) public constant returns (address _erc20, uint256 _value) { + return (erc20,items[_item].price); + } + + function canBuy(address _who, uint256 _item) public constant returns (bool _canBuy) { + return (items[_item].category>0) && + !(items[_item].category==1&&items[_item].customer[_who]>0) && + (items[_item].stockCount>0); + } + + function isBuyer(address _who, uint256 _item) public constant returns (bool _buyer) { + return (items[_item].category==1&&items[_item].customer[_who]>0)||(items[_item].category==2&&safeAdd(items[_item].customer[_who],1 weeks)>now)||(items[_item].category==3&&items[_item].customer[_who]>0); + } + + uint lastWithdrawal; + function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) { + require(safeAdd(lastWithdrawal,1 weeks)<=now); + require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value); + if(_erc20==address(0)) + owner.transfer(_value); + else + ERC20Interface(_erc20).transfer(owner,_value); + lastWithdrawal = now; + return true; + } +} + +contract WalletCenter { + mapping(address=>bool) public wallet; + event Wallet(address indexed _owner, address indexed _wallet); + function createWallet() public returns (address _wallet) { + _wallet = new _Wallet(msg.sender); + wallet[_wallet] = true; + emit Wallet(msg.sender,_wallet); + return _wallet; + } + function isWallet(address _wallet) public constant returns (bool) { + return wallet[_wallet]; + } + mapping(address=>bool) public shop; + event Shop(address indexed _owner, address indexed _shop, address indexed _erc20); + function createShop(address _erc20) public returns (address _shop) { + _shop = new _Shop(msg.sender,_erc20); + shop[_shop] = true; + emit Shop(msg.sender,_shop,_erc20); + return _shop; + } + function isShop(address _shop) public constant returns (bool) { + return shop[_shop]; + } +} +``` +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 33454e3e749bb24e789558c9801f56555fbb64cb Mon Sep 17 00:00:00 2001 From: Nitro888 Date: Mon, 25 Jun 2018 20:10:09 +0900 Subject: [PATCH 027/177] change directory (#1183) --- eip-1175.md => EIPS/eip-1175.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename eip-1175.md => EIPS/eip-1175.md (100%) diff --git a/eip-1175.md b/EIPS/eip-1175.md similarity index 100% rename from eip-1175.md rename to EIPS/eip-1175.md From ed621645c8f3bc5756492f327cda015f35d9f8da Mon Sep 17 00:00:00 2001 From: Nate Welch Date: Tue, 26 Jun 2018 09:06:13 -0400 Subject: [PATCH 028/177] Automatically merged updates to draft EIP(s) 1167 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1167.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index 9e181d50..1e24d58e 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -1,7 +1,7 @@ --- eip: 1167 title: Minimal Proxy Contract -author: Peter Murray (@yarrumretep), Nate Welsh (@flygoing), Joe Messerman (@JAMesserman) +author: Peter Murray (@yarrumretep), Nate Welch (@flygoing), Joe Messerman (@JAMesserman) discussions-to: https://github.com/optionality/clone-factory/issues/10 status: Draft type: Standards Track From 0fe9ffabd6b2c081dbdae141870a43f1d447854c Mon Sep 17 00:00:00 2001 From: Domino Valdano Date: Wed, 27 Jun 2018 04:27:26 -0700 Subject: [PATCH 029/177] Automatically merged updates to draft EIP(s) 758 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-758.md | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/EIPS/eip-758.md b/EIPS/eip-758.md index b828425f..8d7df949 100644 --- a/EIPS/eip-758.md +++ b/EIPS/eip-758.md @@ -1,6 +1,6 @@ --- eip: 758 -title: Subscriptions and filters for transaction return data +title: Subscriptions and filters for completed transactions author: Jack Peterson type: Standards Track category: Interface @@ -9,30 +9,50 @@ created: 2017-11-09 --- ## Simple Summary -Provide a way for external callers to access the return data of functions executed during Ethereum transactions. +Provide a way for external callers to be notified of completed transactions, and access the return data of functions executed when a transaction is mined. ## Abstract -When a new transaction is submitted successfully to an Ethereum node, the node responds with the transaction's hash. If the transaction involved the execution of a contract function that returns data, the data is discarded. If the return data is state-dependent, which is common, there is no straightforward way for the caller to access or compute the return data. This EIP proposes that callers should be able to subscribe to (or poll for) the return data of their transactions. The Ethereum node then sends the return data to the caller when the transactions are sealed. +When a new transaction is submitted successfully to an Ethereum node, the node responds with the transaction's hash. If the transaction involved the execution of a contract function that returns data, the data is discarded. If the return data is state-dependent, which is common, there is no straightforward way for the caller to access or compute the return data. This EIP proposes that callers should be able to subscribe to (or poll for) completed transactions. The Ethereum node sends the return data to the caller when the transactions are sealed. ## Motivation -External callers presently have no way of accessing return data from Ethereum, if the function was executed via `eth_sendTransaction` or `eth_sendRawTransaction` RPC request. Access to function return data is in many cases a desirable feature. Making return data available to external callers also addresses the inconsistency between internal callers, which have access to return data within the context of the transaction, and external callers, which do not. Presently, a common workaround is to log the return data, which is bad for several reasons: it contributes to chain bloat, imposes additional gas costs on the caller, and can result in unused logs being written if the externally called function involves other (internal) function calls that log their return data. +External callers presently have no way of accessing return data from Ethereum, if the function was executed via `eth_sendTransaction` or `eth_sendRawTransaction` RPC request. Access to function return data is in many cases a desirable feature. Making return data available to external callers also addresses the inconsistency between internal callers, which have access to return data within the context of the transaction, and external callers, which do not. Presently, a common workaround is to log the return data, which is bad for several reasons: it contributes to chain bloat, imposes additional gas costs on the caller, and can result in unused logs being written if the externally called function involves other (internal) function calls that log their return data. While implementing the original version of this EIP, it was decided to expand this functionality slightly to allow for external callers to be notified of their completed transactions even in the case where there is *no* return data. This could be either because the method called doesn't return a value, or because the transaction is a simple transfer of value. ## Specification ### Subscription -A caller who wants to be notified of return data for their transactions sends an `eth_subscribe` RPC request with the parameter `"returnData"`: +A caller who wants to be notified when transactions of theirs complete sends an `eth_subscribe` RPC request with the first parameter `"completedTransaction"`: ```json -{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["returnData"]} +{"jsonrpc": "2.0", "id": 1, "method": "eth_subscribe", "params": ["completedTransaction", filter]} ``` -The Ethereum node responds with a subscription ID: +The `filter` parameter is a dictionary containing 3 optional named arguments: `from`, `to`, and `hasReturnData`. `from` and `to` can each either be single addresses, or a list of addresses. They are used to filter out any transactions not sent from an address in the `from` list and sent to an address in the to list. `hasReturnData` is a boolean--if it is specified and `true`, then notifications will be received only for completed transactions containing returnData. + +For example, to restrict results to contract creations originating from either of two addresses (0x3f7d39bDBf1f5cE649c194571aEd3D2BbB2F85ce or 0x7097f41F1C1847D52407C629d0E0ae0fDD24fd58): + +```json +filter = { "from" : ["0x3f7d39bDBf1f5cE649c194571aEd3D2BbB2F85ce", + "0x7097f41F1C1847D52407C629d0E0ae0fDD24fd58"], + "to" : "0x0" + } +``` + +To restrict results to method calls on contract address 0xD9Cb531aB97A652c8fC60dcF6D263fcA2F5764e9: +```json +filter = { "to" : "0xD9Cb531aB97A652c8fC60dcF6D263fcA2F5764e9", "hasReturnData" : true } +``` +Or to be notified of any transactions submitted by this rpc client when they complete, with no further restrictions: +```json +filter = {} +``` + +After the request is recieved, the Ethereum node responds with a subscription ID: ```json {"jsonrpc": "2.0", "id": 1, "result": "0x00000000000000000000000000000b0b"} ``` -The caller submits a transaction via `eth_sendTransaction` or `eth_sendRawTransaction` RPC request which has the transaction hash `"0x00000000000000000000000000000000000000000000000000000000deadbeef"`. When the transaction is sealed (mined), the Ethereum node computes the return value (`"0x000000000000000000000000000000000000000000000000000000000000002a"`) and pushes a notification to the caller: +Suppose the caller then submits a transaction via `eth_sendTransaction` or `eth_sendRawTransaction` RPC request which has the transaction hash `"0x00000000000000000000000000000000000000000000000000000000deadbeef"`. When the transaction is sealed (mined), the Ethereum node pushes a notification to the caller. If the transaction is a method call on a contract, this will include the return value (eg. `"0x000000000000000000000000000000000000000000000000000000000000002a"`) of the called function: ```json { @@ -48,17 +68,17 @@ The caller submits a transaction via `eth_sendTransaction` or `eth_sendRawTransa } ``` -The caller receives notifications about their transactions' return data in two cases: first when a transaction is sealed, and again (with an extra `"removed": true` field) if a transaction is affected by a chain reorganization. Notifications are sent to the client for all transactions submitted from the client that are sealed _after_ subscribing. As with other subscriptions, the caller can send an `eth_unsubscribe` RPC request to stop receiving push notifications: +The caller receives notifications about their transactions in two cases: first when a transaction is sealed, and again (with an extra `"removed": true` field) if a transaction is affected by a chain reorganization. Notifications are sent to the client for all transactions submitted from the client that are sealed _after_ subscribing. If `from`, `to`, or `hasReturnData` is specified, then only those matching the filter criteria will generate notificaitons. As with other subscriptions, the caller can send an `eth_unsubscribe` RPC request to stop receiving push notifications: ```json {"jsonrpc": "2.0", "id": 2, "method": "eth_unsubscribe", "params": ["0x00000000000000000000000000000b0b"]} ``` ### Polling -Push notifications require full duplex connections (i.e., websocket or IPC). Instead of subscribing, callers using HTTP send an `eth_newReturnDataFilter` request: +Push notifications require full duplex connections (i.e., websocket or IPC). Instead of subscribing, callers using HTTP send an `eth_newCompletedTransactionFilter` request: ```json -{"jsonrpc": "2.0", "id": 1, "method": "eth_newReturnDataFilter"} +{"jsonrpc": "2.0", "id": 1, "method": "eth_newCompletedTransactionFilter", "params": [filter] } ``` The Ethereum node responds with a filter ID: @@ -67,7 +87,7 @@ The Ethereum node responds with a filter ID: {"jsonrpc": "2.0", "id": 1, "result": "0x1"} ``` -When a transaction is submitted, the Ethereum node computes the return data and pushes it to a queue, which is emptied when the caller polls using `eth_getFilterChanges`: +When a transaction is submitted, the Ethereum node pushes the transaction notification, including return value, into a queue which is emptied when the caller polls using `eth_getFilterChanges`: ```json {"jsonrpc": "2.0", "id": 2, "method": "eth_getFilterChanges", "params": ["0x1"]} @@ -86,7 +106,7 @@ The node responds with an array of transaction hashes and their corresponding re } ``` -All transactions submitted by the client that were sealed _after_ the initial `eth_newReturnDataFilter` request are included in this array. +All transactions that were sealed _after_ the initial `eth_newCompletedTransactionFilter` request are included in this array. Again, if the `filter` param is a non-empty dictinary (contains either `from`, `to`, or `hasReturnData`) then only transactions matching the filter criteria generate notifications. Note that in the polling case, there is no way for the Ethereum node to be sure that an RPC client which submits a transaction was the same as the one who created the filter, so there is no restriction based on where the transaction was submitted. ## Rationale From 02cf4dd59c0ff1630f1f4df35969f49aeb226b16 Mon Sep 17 00:00:00 2001 From: Alan Lu Date: Thu, 28 Jun 2018 08:41:35 -0500 Subject: [PATCH 030/177] Oracle interface (#1154) * Started writing proposal * Added more commentary and removed comments * Update and rename eip-oracle_interface.md to eip-1154.md * Add discussions-to URL * Corrected discussions-to link * Expand on use cases and types of oracles supported --- EIPS/eip-1154.md | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 EIPS/eip-1154.md diff --git a/EIPS/eip-1154.md b/EIPS/eip-1154.md new file mode 100644 index 00000000..ad4437ec --- /dev/null +++ b/EIPS/eip-1154.md @@ -0,0 +1,101 @@ +--- +eip: 1154 +title: Oracle Interface +author: Alan Lu (@cag) +discussions-to: https://github.com/ethereum/EIPs/issues/1161 +status: Draft +type: Standards Track +category: ERC +created: 2018-06-13 +--- + +## Simple Summary +A standard interface for oracles. + +## Abstract +In order for ethereum smart contracts to interact with off-chain systems, oracles must be used. These oracles report values which are normally off-chain, allowing smart contracts to react to the state of off-chain systems. A distinction and a choice is made between push and pull based oracle systems. Furthermore, a standard interface for oracles is described here, allowing different oracle implementations to be interchangeable. + +## Motivation +The Ethereum ecosystem currently has many different oracle implementations available, but they do not provide a unified interface. Smart contract systems would be locked into a single set of oracle implementations, or they would require developers to write adapters/ports specific to the oracle system chosen in a given project. + +Beyond naming differences, there is also the issue of whether or not an oracle report-resolving transaction _pushes_ state changes by calling affected contracts, or changes the oracle state allowing dependent contracts to _pull_ the updated value from the oracle. These differing system semantics could introduce inefficiencies when adapting between them. + +Ultimately, the value in different oracle systems comes from their underlying resolution mechanics, and points where these systems are virtually identical should be standardized. + +These oracles may be used for answering questions about "real-world events", where each ID can be correlated with a specification of a question and its answers (so most likely for prediction markets, basically). + +Another use case could be for decision-making processes, where the results given by the oracle represent decisions made by the oracle (e.g. futarchies). DAOs may require their use in decision making processes. + +Both the ID and the results are intentionally unstructured so that things like time series data (via splitting the ID) and different sorts of results (like one of a few, any subset of up to 256, or some value in a range with up to 256 bits of granularity) can be represented. + +## Specification + +

    +
    Oracle
    +
    An entity which reports data to the blockchain.
    + +
    Oracle handler
    +
    A smart contract which receives data from an oracle.
    + +
    ID
    +
    A way of indexing the data which an oracle reports. May be derived from or tied to a question for which the data provides the answer.
    + +
    Result
    +
    Data associated with an id which is reported by an oracle. This data oftentimes will be the answer to a question tied to the id. Other equivalent terms that have been used include: answer, data, outcome.
    +
    + +```solidity +interface OracleHandler { + function receiveResult(bytes32 id, bytes32 result) external; +} +``` + +`receiveResult` MUST revert if the `msg.sender` is not an oracle authorized to provide the `result` for that `id`. + +`receiveResult` MUST revert if `receiveResult` has been called with the same `id` before. + +`receiveResult` MAY revert if the `id` or `result` cannot be handled by the handler. + +The oracle can be any Ethereum account. + +## Rationale +The specs are currently very similar to what is implemented by ChainLink (which can use any arbitrarily-named callback) and Oraclize (which uses `__callback`). + +With this spec, the oracle _pushes_ state to the handler, which must react accordingly to the updated state. An alternate _pull_-based interface can be prescribed, as follows: + +### Alternate Pull-based Interface +Here are alternate specs loosely based on Gnosis prediction market contracts v1. Reality Check also exposes a similar endpoint (`getFinalAnswer`). + +```solidity +interface Oracle { + function resultFor(bytes32 id) external view returns (bytes32 result); +} +``` + +`resultFor` MUST revert if the result for an `id` is not available yet. + +`resultFor` MUST return the same result for an `id` after that result is available. + +### Push vs Pull +Note that push-based interfaces may be adapted into pull-based interfaces. Simply deploy an oracle handler which stores the result received and implements `resultFor` accordingly. + +Similarly, every pull-based system can be adapted into a push-based system: just add a method on the oracle smart contract which takes an oracle handler address and calls `receiveResult` on that address. + +In both cases, an additional transaction would have to be performed, so the choice to go with push or pull should be based on the dominant use case for these oracles. + +In the simple case where a single account has the authority to decide the outcome of an oracle question, there is no need to deploy an oracle contract and store the outcome on that oracle contract. Similarly, in the case where the outcome comes down to a vote, existing multisignature wallets can be used as the authorized oracle. + +#### Multiple Oracle Handlers +In the case that many oracle handlers depend on a single oracle result and all these handlers expect the result to be pushed to them, the push and pull adaptations mentioned before may be combined if the pushing oracle cannot be trusted to send the same result to every handler (in a sense, this forwards the trust to the oracle adaptor implementation). + +In a pull-based system, each of the handlers would have to be called to pull the result from the oracle contract, but in the proposed push-based system, the adapted oracle would have to be called to push the results to each of the handlers. + +Transaction-wise, both systems are roughly equivalent in efficiency in this scenario, but in the push-based system, there's a need for the oracle handlers to store the results again, whereas in the pull-based system, the handlers may continue to refer to the oracle for the results. Although this may be somewhat less efficient, requiring the handlers to store the results can also provide security guarantees, especially with regards to result immutability. + +#### Result Immutability +In both the proposed specification and the alternate specification, results are immutable once they are determined. This is due to the expectation that typical handlers will require results to be immutable in order to determine a resulting state consistently. With the proposed push-based system, the handler enforces the result immutability requirement, whereas in the alternate pull-based system, either the oracle would have to be trusted to implement the spec correctly and enforce the immutability requirement, or the handler would also have to handle result immutability. + +For data which mutates over time, the `id` field may be structured to specify "what" and "when" for the data (using 128 bits to specify "when" is still safe for many millenia). + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From a9975c84e800cc15180dab152e45d73f8e167492 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Fri, 29 Jun 2018 15:41:08 +0200 Subject: [PATCH 031/177] Automatically merged updates to draft EIP(s) 1109 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1109.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/EIPS/eip-1109.md b/EIPS/eip-1109.md index 07c408cd..c8c69e45 100644 --- a/EIPS/eip-1109.md +++ b/EIPS/eip-1109.md @@ -46,6 +46,10 @@ This EIP is backwards compatible. Smart contracts that call precompiled contrac - Call to defined precompiled contract with a value!=0 on the call. - Call to undefined precompiled contract with a value!=0 on the call. - Normal call to precompiled contract with remaining gas<700 but gas>[the cost of the call]. +- Call to a precompiled contract which forwards all gas but + * 600 + * 700 + * 701 ## Implementation From 26a4fbc197f129324e3f8c4939e388590a379362 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Tue, 3 Jul 2018 09:56:21 -0400 Subject: [PATCH 032/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 77 +++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 7d12c6c5..e8d4b564 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -1,6 +1,6 @@ --- eip: 1102 -title: Opt-in web3 access +title: Opt-in provider access author: Paul Bouchon discussions-to: https://ethereum-magicians.org/t/opt-in-web3-access/414 status: Draft @@ -11,13 +11,13 @@ created: 2018-05-04 ## Simple summary -This proposal describes a new way for DOM environments to expose the web3 API that requires user approval. +This proposal describes a way for DOM environments to expose an Ethereum provider API that requires user approval. ## Abstract -MetaMask and most other tools that provide access to web3-enabled environments do so automatically and without user consent. This exposes users of such environments to fingerprinting attacks since untrusted websites can check for a `web3` object and reliably identify web3-enabled clients. +The previous generation of Ethereum-enabled DOM environments follows a pattern of directly injecting a provider object into the DOM without user consent. This exposes users of such environments to fingerprinting attacks since untrusted websites can check for the injected provider and reliably identify Ethereum-enabled clients. -This proposal outlines a new protocol in which dapps request access to the web3 API instead of relying on its preexistence in a given DOM environment. +This proposal outlines a protocol in which dapps request access to an Ethereum provider API. ## Specification @@ -35,69 +35,60 @@ IF web3 is undefined ``` START dapp -IF web3 is defined - CONTINUE dapp -IF web3 is undefined - REQUEST[1] web3 API +REQUEST[1] Ethereum provider IF user approves - INJECT[2] web3 API - NOTIFY[3] dapp + NOTIFY[2] dapp CONTINUE dapp IF user rejects - IF non-web3 environment - NOOP[4] + IF non-Ethereum environment + NOOP[3] ``` #### `[1] REQUEST` -Dapps MUST request the web3 API by sending a message using [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of “WEB3_API_REQUEST” and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK". +Dapps MUST request an Ethereum provider API by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of `ETHEREUM_PROVIDER_REQUEST`. -#### `[2] INJECT` +#### `[2] NOTIFY` -Dapp browsers should inject the web3 API using an implementation-specific strategy that can expose the web3 API to the user’s browser context, such as HTML script tag injection. +Ethereum-enabled DOM environments MUST notify dapps of successful provider API exposure by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of `ETHEREUM_PROVIDER_SUCCESS` and an `ethereum` property containing an Ethereum provider object that conforms to [ethereum/interfaces#16](https://github.com/ethereum/interfaces/issues/16). -#### `[3] NOTIFY` +#### `[3] NOOP` -Dapps browsers MUST notify dapps of successful web3 exposure by sending a message using [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of “WEB3_API_SUCCESS" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK". - -#### `[4] NOOP` - -If a user rejects web3 access on an untrusted site, the site itself MUST NOT be notified in any way; notification of a rejection would allow third-party tools to still identify that a client is web3-enabled despite not being granted web3 access. +If a user rejects access to the Ethereum provider API on an untrusted site, the site itself MUST NOT be notified in any way; notification of a rejection would allow third-party tools to still identify that a client is Ethereum-enabled despite not being granted access to any provider API. ### Example implementation: `postMessage` -The following example demonstrates one possible implementation of this strategy in a browser-based DOM environment. Note that web3 environments on other platforms would most likely use platform-specific native messaging protocols, not `postMessage`. +The following example demonstrates one possible implementation of this strategy in a browser-based DOM environment. Note that Ethereum-enabled environments on other platforms would most likely use platform-specific native messaging protocols, not `postMessage`. ```js -if (typeof web3 !== 'undefined') { - // web3 API defined, start dapp -} else { - window.addEventListener('message', function (event) { - if (!event.data || !event.data.type) { return; } - if (event.data.type === 'WEB3_API_SUCCESS') { - // web3 API defined, start dapp - } - }); - // request web3 API - window.postMessage({ type: 'WEB3_API_REQUEST' }); -} +// Listen for provider API +window.addEventListener('message', function (event) { + if (!event.data || !event.data.type) { return; } + if (event.data.type === 'ETHEREUM_PROVIDER_SUCCESS') { + // Provider API exposed, continue + const networkVersion = await event.data.ethereum.send('net_version', []); + console.log(networkVersion); + } +}); +// Request Provider API +window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }); ``` ## Rationale -An [open issue](https://github.com/MetaMask/metamask-extension/issues/714) against the [MetaMask](https://github.com/MetaMask/metamask-extension) extension has received community upvotes and Richard Burton of the [Balance](https://github.com/balance-io) team published a well-received [blog post](https://medium.com/@ricburton/metamask-walletconnect-js-b47857efb4f7) discussing these potential changes. +The pattern of provider auto-injection followed by the previous generation of Ethereum-enabled DOM environements failed to protect user privacy by allowing untrusted websites to uniquely identify Ethereum users. This proposal establishes a new pattern wherein dapps must request access to an Ethereum provider API. This protocol directly prevents fingerprinting attacks by giving users the ability to reject provider exposure on a given website. ### Constraints -* Web3 MUST NOT be exposed to websites by default. -* Dapps MUST request web3 if it does not exist. -* Users MUST be able to approve or reject web3 access. -* Web3 MUST be exposed to websites after user consent. -* Environments MAY continue auto-exposing web3 if users can disable this behavior. +* A provider API MUST NOT be exposed to websites by default. +* Dapps MUST request a provider API if it does not exist. +* Users MUST be able to approve or reject provider API access. +* A provider API MUST be exposed to websites after user consent. +* Environments MAY continue auto-exposing a provider API if users can opt-out. ### Immediate value-add -* Users can reject web3 access on untrusted sites to prevent web3 fingerprinting. +* Users can reject provider API access on untrusted sites to prevent fingerprinting. ### Long-term value-add @@ -108,11 +99,11 @@ An [open issue](https://github.com/MetaMask/metamask-extension/issues/714) again ## Backwards compatibility -This proposal impacts dapp authors and requires that they request access to the web3 API before using it. This proposal also impacts developers of web3-enabled environments or dapp browsers as these tools should no longer auto-expose the web3 API; instead, they should only do so if a website requests the API and if the user consents to its access. Environments may continue to auto-expose the web3 API as long as users have the ability to disable this behavior. +This proposal impacts dapp authors and requires that they request access to an Ethereum provider API before using it. This proposal also impacts developers of Ethereum-enabled environments or dapp browsers as these tools should no longer auto-expose any provider API; instead, they should only do so if a website requests a provider API and if the user consents to its access. Environments may continue to auto-expose an Ethereum provider API as long as users have the ability to disable this behavior. ## Implementation -The MetaMask team is currently working an [MVP implementation](https://github.com/MetaMask/metamask-extension/issues/3930) of the strategy described above and expects to begin limited user testing soon. +The MetaMask team is currently working an [MVP implementation](https://github.com/MetaMask/metamask-extension/pull/4703) of the strategy described above and expects to begin limited user testing soon. ## Copyright From 80324667015eb1cbd7535426cc4a821334cf3886 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Tue, 3 Jul 2018 17:42:37 -0400 Subject: [PATCH 033/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index e8d4b564..41732c37 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -2,7 +2,7 @@ eip: 1102 title: Opt-in provider access author: Paul Bouchon -discussions-to: https://ethereum-magicians.org/t/opt-in-web3-access/414 +discussions-to: https://ethereum-magicians.org/t/eip-1102-opt-in-provider-access/414 status: Draft type: Standards Track category: Interface @@ -37,22 +37,27 @@ IF web3 is undefined START dapp REQUEST[1] Ethereum provider IF user approves - NOTIFY[2] dapp + INJECT[2] provider API + NOTIFY[3] dapp CONTINUE dapp IF user rejects IF non-Ethereum environment - NOOP[3] + NOOP[4] ``` #### `[1] REQUEST` -Dapps MUST request an Ethereum provider API by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of `ETHEREUM_PROVIDER_REQUEST`. +Dapps MUST request an Ethereum provider API by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of ETHEREUM_PROVIDER_REQUEST and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK". -#### `[2] NOTIFY` +#### `[2] INJECT` -Ethereum-enabled DOM environments MUST notify dapps of successful provider API exposure by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of `ETHEREUM_PROVIDER_SUCCESS` and an `ethereum` property containing an Ethereum provider object that conforms to [ethereum/interfaces#16](https://github.com/ethereum/interfaces/issues/16). +Ethereum-enabled DOM environments MUST expose an Ethereum provider API as a global `ETHEREUM_PROVIDER` variable on the `window` object. -#### `[3] NOOP` +#### `[3] NOTIFY` + +Ethereum-enabled DOM environments MUST notify dapps of successful provider API exposure by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of ETHEREUM_PROVIDER_SUCCESS" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK" + +#### `[4] NOOP` If a user rejects access to the Ethereum provider API on an untrusted site, the site itself MUST NOT be notified in any way; notification of a rejection would allow third-party tools to still identify that a client is Ethereum-enabled despite not being granted access to any provider API. @@ -61,12 +66,12 @@ If a user rejects access to the Ethereum provider API on an untrusted site, the The following example demonstrates one possible implementation of this strategy in a browser-based DOM environment. Note that Ethereum-enabled environments on other platforms would most likely use platform-specific native messaging protocols, not `postMessage`. ```js -// Listen for provider API +// Listen for provider API exposure window.addEventListener('message', function (event) { if (!event.data || !event.data.type) { return; } if (event.data.type === 'ETHEREUM_PROVIDER_SUCCESS') { // Provider API exposed, continue - const networkVersion = await event.data.ethereum.send('net_version', []); + const networkVersion = await ETHEREUM_PROVIDER.send('net_version', []); console.log(networkVersion); } }); From 283c96af1ce25d21e38721e911f52dcf7ed35621 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Thu, 5 Jul 2018 08:07:29 -0400 Subject: [PATCH 034/177] Show canonical URLs (#1192) --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 23ee9b4b..723838b9 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,12 @@ When you believe your EIP is mature and ready to progress past the draft phase, - **For a Standards Track EIP of type Core**, ask to have your issue added to [the agenda of an upcoming All Core Devs meeting](https://github.com/ethereum/pm/issues), where it can be discussed for inclusion in a future hard fork. If implementers agree to include it, the EIP editors will update the state of your EIP to 'Accepted'. - **For all other EIPs**, open a PR changing the state of your EIP to 'Final'. An editor will review your draft and ask if anyone objects to its being finalised. If the editor decides there is no rough consensus - for instance, because contributors point out significant issues with the EIP - they may close the PR and request that you fix the issues in the draft before trying again. -# EIP status terms +# EIP Status Terms * **Draft** - an EIP that is open for consideration. * **Accepted** - an EIP that is planned for immediate adoption, i.e. expected to be included in the next hard fork (for Core/Consensus layer EIPs). * **Final** - an EIP that has been adopted in a previous hard fork (for Core/Consensus layer EIPs). * **Deferred** - an EIP that is not being considered for immediate adoption. May be reconsidered in the future for a subsequent hard fork. + +# Preferred Citation Format + +The canonical URL for a EIP that has achieved draft status at any point is at https://eips.ethereum.org/. For example, the canonical URL for ERC-165 is https://eips.ethereum.org/EIPS/eip-165. From 7064bc39536f7e4b2da23af77c44c7fd6b84cf49 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Thu, 5 Jul 2018 11:08:00 -0400 Subject: [PATCH 035/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 41732c37..4a02acc4 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -36,13 +36,13 @@ IF web3 is undefined ``` START dapp REQUEST[1] Ethereum provider - IF user approves - INJECT[2] provider API - NOTIFY[3] dapp - CONTINUE dapp - IF user rejects - IF non-Ethereum environment - NOOP[4] +IF user approves + INJECT[2] provider API + NOTIFY[3] dapp + CONTINUE dapp +IF user rejects +IF non-Ethereum environment + NOOP[4] ``` #### `[1] REQUEST` From a9794af41b5bbffb17391a37d807266baf4d2df9 Mon Sep 17 00:00:00 2001 From: Juliano Rizzo Date: Wed, 11 Jul 2018 12:15:40 -0300 Subject: [PATCH 036/177] EIP-1191: extends EIP-55 by optionally adding a chain id defined by EIP-155 to the checksum calculation (#1191) * EIP Add chain id to mixed-case checksum address encoding * Fixes header * Update eip-1186.md * Change assigned EIP number --- EIPS/eip-1191.md | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 EIPS/eip-1191.md diff --git a/EIPS/eip-1191.md b/EIPS/eip-1191.md new file mode 100644 index 00000000..871127b5 --- /dev/null +++ b/EIPS/eip-1191.md @@ -0,0 +1,97 @@ +--- +eip: 1191 +title: Add chain id to mixed-case checksum address encoding +author: Juliano Rizzo (@juli) +status: Draft +type: Standards Track +category: ERC +created: 2018-03-18 +requires: 55, 155 +discussions-to: https://github.com/ethereum/EIPs/issues/1121 +--- +## Simple Summary +This EIP extends EIP-55 by optionally adding a chain id defined by EIP-155 to the checksum calculation. + +## Specification +Convert the address using the same algorithm defined by EIP-55 but if a registered chain id is provided, add it to the input of the hash function. If the chain id passed to the function belongs to a network that opted for using this checksum variant, prefix the address with the chain id and the `0x` separator before calculating the hash. Then convert the address to hexadecimal, but if the ith digit is a letter (ie. it's one of `abcdef`) print it in uppercase if the 4*ith bit of the calculated hash is 1 otherwise print it in lowercase. + +## Rationale + 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 +from sha3 import keccak_256 +import random +""" + addr (str): Hexadecimal address, 40 characters long with 2 characters prefix + chainid (int): chain id from EIP-155 """ +def eth_checksum_encode(addr, chainid=1): + adopted_eip1191 = [30, 31] + hash_input = str(chainid) + addr.lower() if chainid in adopted_eip1191 else addr[2:].lower() + hash_output = keccak_256(hash_input.encode('utf8')).hexdigest() + aggregate = zip(addr[2:].lower(),hash_output) + out = addr[:2] + ''.join([c.upper() if int(a,16) >= 8 else c for c,a in aggregate]) + return out +``` +## Test Cases +```python +eth_mainnet= [ +'0x88021160C5C792225E4E5452585947470010289D', +'0x27b1fdb04752bbc536007a920d24acb045561c26', +'0x52908400098527886E0F7030069857D2E4169EE7', +'0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', +'0x8617E340B3D01FA5F11F306F4090FD50E238070D', +'0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', +'0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB', +'0xde709f2102306220921060314715629080e2fb77', +'0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359', +] +rsk_mainnet = [ +'0x6549F4939460DE12611948B3F82B88C3C8975323', +'0x27b1FdB04752BBc536007A920D24ACB045561c26', +'0x3599689E6292B81B2D85451025146515070129Bb', +'0x52908400098527886E0F7030069857D2E4169ee7', +'0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD', +'0x8617E340b3D01Fa5f11f306f4090fd50E238070D', +'0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB', +'0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB', +'0xDe709F2102306220921060314715629080e2FB77', +'0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359', +] +rsk_testnet= [ +'0x42712D45473476B98452F434E72461577D686318', +'0x27B1FdB04752BbC536007a920D24acB045561C26', +'0x3599689e6292b81b2D85451025146515070129Bb', +'0x52908400098527886E0F7030069857D2e4169EE7', +'0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd', +'0x66f9664F97F2b50f62d13eA064982F936DE76657', +'0x8617e340b3D01fa5F11f306F4090Fd50e238070d', +'0xDE709F2102306220921060314715629080e2Fb77', +'0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359', +'0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB', +'0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB', +] +test_cases = {30 : rsk_mainnet, 31 : rsk_testnet, 1 : eth_mainnet} + +for chainid, cases in test_cases.items(): + for addr in cases: + assert ( addr == eth_checksum_encode(addr,chainid) ) +``` +## Adoption +### Adoption Table +| Network | Chain id | Supports this EIP | +|--------------|----------|-------------------| +| RSK Mainnet | 30 | Yes | +| RSK Testnet | 31 | Yes | + + +| Wallet | Implements this EIP| +|--------------|--------------------| +| MyCrypto | In progress | +| Ledger | In progress | +| Trezor | In progress | + From 3963129ad5655c9e082577b7804f33751184b4a7 Mon Sep 17 00:00:00 2001 From: Pavel Motyrev Date: Thu, 12 Jul 2018 22:43:40 +0700 Subject: [PATCH 037/177] Automatically merged updates to draft EIP(s) 823 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-823.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/EIPS/eip-823.md b/EIPS/eip-823.md index 235c4a03..727af0cf 100644 --- a/EIPS/eip-823.md +++ b/EIPS/eip-823.md @@ -95,11 +95,11 @@ NOTE: Callers MUST handle false from returns (bool success). Callers MUST NOT as ##### __targetExchangeCallback This function is called by the intermediate exchange service contract. This function should add `_amount` tokens of the target contract to the exchangers address for exchange to be completed successfully. -s + NOTE: It is required that only the exchange service contract has the authority to call this function. ``` js -function __targetExchangeCallback (uint _amount) public returns(bool success) +function __targetExchangeCallback (uint _to, uint _amount) public returns(bool success) ``` ##### __targetExchangeAndSpendCallback @@ -108,7 +108,7 @@ This function is called by the intermediate exchange service contract. This func NOTE: It is required that only the exchange service contract has the authority to call this function. ``` js -function __targetExchangeAndSpendCallback (address _to,uint _amount) public returns(bool success) +function __targetExchangeAndSpendCallback (address _from, address _to, uint _amount) public returns(bool success) ``` #### Events @@ -156,7 +156,7 @@ function registerToken(address _token) public returns(bool success) This function is called by the token holder who wants to exchange his token with the `_targetContract` tokens. This function queries the exchange rate, calculates the converted amount, calls `__exchangerCallback` and calls the `__targetExchangeCallback`. It takes address of the target contract and amount to exchange as parameter and returns boolean `success` and amount credited. ``` js -function exchangeToken(address _targetContract, uint _amount) public returns(bool success, uint creditedAmount) +function exchangeToken(address _targetContract, uint _amount, address _from) public returns(bool success, uint creditedAmount) ``` ##### exchangeAndSpend @@ -164,7 +164,7 @@ function exchangeToken(address _targetContract, uint _amount) public returns(boo This function is called by the token holder who wants to exchange his token with the `_targetContract` tokens. This function queries the exchange rate, calculates the converted amount, calls `__exchangerCallback` and calls the `__targetExchangeAndSpendCallback`. It takes address of the target contract and amount to exchange as parameter and returns boolean `success` and amount credited. ``` js -function exchangeAndSpend(address _targetContract, uint _amount,address _to) public returns(bool success) +function exchangeAndSpend(address _targetContract, uint _amount, address _from, address _to) public returns(bool success) ``` #### Events From 05c1c0d317759c389ec04b777690bdf2c4d88a51 Mon Sep 17 00:00:00 2001 From: Brooklyn Zelenka Date: Sat, 14 Jul 2018 17:53:40 +0200 Subject: [PATCH 038/177] Automatically merged updates to draft EIP(s) 1066 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1066.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-1066.md b/EIPS/eip-1066.md index c4865c98..fd09ad09 100644 --- a/EIPS/eip-1066.md +++ b/EIPS/eip-1066.md @@ -578,7 +578,9 @@ function appCode(Sleep _state) returns (byte code) { ## Implementation -Reference cases and helper library can be found [here](https://github.com/Finhaven/EthereumStatusCodes) +Reference cases and helper libraries (Solidity and JS) can be found at: +* [Source Code](https://github.com/expede/ethereum-status-codes/) +* [Package on npm](https://www.npmjs.com/package/ethereum-status-codes) ## Copyright From 1fe745243328c3d0ff830d2673d8e37569197de1 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Tue, 17 Jul 2018 03:50:16 -0700 Subject: [PATCH 039/177] EIP 1193: Ethereum Provider API (#1193) * Add EIP 2020: Ethereum Provider * Incorporate changes into EIP 1102 (thanks Paul) * Update discussions-to link * Clarify intentions * Proper property names * Updates * Requires EIP 1102 * Updates * Updates * Merge EIP 1102 updates * Update postMessage types to align with EIP 1102 * Remove meta provider information, since all the data is accessible through the JSON-RPC API * Updates * Usage => Examples * Update example console msg * Fix subheadings * Use console.error instead of console.log in error instances * Updates * Updates * Add params for subscriptions * Clarifying subscription response * Update subscriptions spec * Fix * More clear example headings * Better wording for `close` event spec * Updates * Remove unnecessary spacing * Updates * Better grammar * Formatting * Updates * Better unsubscribe documentation * Updates * Typo (pluralize) * Better wording * periods * Use pull number * Update discussions-to link * wording --- EIPS/eip-1193.md | 265 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 EIPS/eip-1193.md diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md new file mode 100644 index 00000000..b2e0e1a2 --- /dev/null +++ b/EIPS/eip-1193.md @@ -0,0 +1,265 @@ +--- +eip: 1193 +title: Ethereum Provider API +author: Ryan Ghods (@ryanio), Marc Garreau (@marcgarreau) +discussions-to: https://ethereum-magicians.org/t/eip-1193-ethereum-provider/640 +status: Draft +type: Standards Track +category: Interface +created: 2018-06-30 +requires: 1102 +--- + +## Summary + +This proposal formalizes an Ethereum Provider API. + +The provider is designed to be minimal, containing 3 methods: `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`. + +## API + +### Send + +```js +ethereum.send(method: String, params?: Array): Promise; +``` + +Promise resolves with `result` or rejects with `Error`. + +See the [available methods](https://github.com/ethereum/wiki/wiki/JSON-RPC#json-rpc-methods). + +### Subscriptions + +#### Subscribe + +```js +ethereum.subscribe(subscriptionType: String, params?: Array): Promise; +``` + +Promise resolves with `subscriptionId: String` or rejects with `Error`. + +See the [types of subscriptions](https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB#supported-subscriptions). + +Results emit on `subscriptionId` using [EventEmitter](https://nodejs.org/api/events.html). Attach listeners with: + +```js +ethereum.on(subscriptionId, listener: (result: any) => void): this; +``` + +The event emits with `result`, the subscription `result` or an `Error` object. + +#### Unsubscribe + +```js +ethereum.unsubscribe(subscriptionId: String): Promise; +``` + +Promise resolves with `success: Boolean` or rejects with `Error`. + +All [EventEmitter](https://nodejs.org/api/events.html) listeners on `subscriptionId` will also be removed. + +### Events + +Events are emitted using [EventEmitter](https://nodejs.org/api/events.html). + +#### connect + +The provider emits `connect` on connect to a network. + +```js +ethereum.on('connect', listener: () => void): this; +``` + +You can detect which network by sending `net_version`: + +```js +const network = await ethereum.send('net_version'); +> '1' +``` + +#### close + +The provider emits `close` on disconnect from a network. + +```js +ethereum.on('close', listener: (code: Number, reason: String) => void): this; +``` + +The event emits with `code` and `reason`. The code follows the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). + +#### networkChanged + +The provider emits `networkChanged` on connect to a new network. + +```js +ethereum.on('networkChanged', listener: (networkId: String) => void): this; +``` + +The event emits with `networkId`, the new network returned from `net_version`. + +#### accountsChanged + +The provider emits `accountsChanged` if the accounts returned from the provider (`eth_accounts`) changes. + +```js +ethereum.on('accountsChanged', listener: (accounts: Array) => void): this; +``` + +The event emits with `accounts`, an array of the accounts' public keys. + +### Constructor + +```js +ethereum.constructor.name; +> 'EthereumProvider' +``` + +## Examples + +```js +// Request Ethereum Provider (EIP 1102) +window.addEventListener('message', event => { + if (event.data && event.data.type === 'ETHEREUM_PROVIDER_SUCCESS') { + start(window.ethereum); + } +}); +window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, this.origin); + +function start(ethereum) { + // A) Primary use case - set provider in web3.js + web3.setProvider(ethereum); + + // B) Secondary use case - use provider object directly + // Example: Log accounts + ethereum + .send('eth_accounts') + .then(accounts => { + console.log(`Accounts:\n${accounts.join('\n')}`); + }) + .catch(error => { + console.error( + `Error fetching accounts: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); + + // Example: Log last block + ethereum + .send('eth_getBlockByNumber', ['latest', 'true']) + .then(block => { + console.log(`Block ${block.number}:\n${block}`); + }) + .catch(error => { + console.error( + `Error fetching last block: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); + + // Example: Log new blocks + let subId; + ethereum + .subscribe('newHeads') + .then(subscriptionId => { + subId = subscriptionId; + ethereum.on(subscriptionId, block => { + if (result instanceOf Error) { + const error = result; + console.error( + `Error from newHeads subscription: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + } else { + console.log(`New block ${block.number}:\n${block}`); + } + }); + }) + .catch(error => { + console.error( + `Error making newHeads subscription: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); + // to unsubscribe + ethereum + .unsubscribe(subId) + .then(result => { + console.log(`Unsubscribed newHeads subscription ${subscriptionId}`); + }) + .catch(error => { + console.error( + `Error unsubscribing newHeads subscription: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); + + // Example: Log when accounts change + const logAccounts = accounts => { + console.log(`Accounts:\n${accounts.join('\n')}`); + }; + ethereum.on('accountsChanged', logAccounts); + // to unsubscribe + ethereum.removeListener('accountsChanged', logAccounts); + + // Example: Log if connection ends + ethereum.on('close', (code, reason) => { + console.log( + `Ethereum provider connection closed: ${reason}. Code: ${code}` + ); + }); +} +``` + +## Specification + +### Send + +The `send` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object). + +If the Ethereum JSON-RPC API returns a response object with no error, then the Promise **MUST** resolve with the `response.result` object untouched by the implementing Ethereum Provider. + +If the Ethereum JSON-RPC API returns response object that contains an error property then the Promise **MUST** be rejected with an Error object containing the `response.error.message` as the Error message, `response.error.code` as a code property on the error and `response.error.data` as a data property on the error. + +If an error occurs during processing, such as an HTTP error or internal parsing error then the Promise **MUST** be rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. + +If the implementing Ethereum Provider is not talking to an external Ethereum JSON-RPC API provider then it **MUST** resolve with an object that matches the JSON-RPC API object as specified in the [Ethereum JSON-RPC documentation](https://github.com/ethereum/wiki/wiki/JSON-RPC). In case of an error, ensure that the Promise is rejected with an Error that matches the above shape. + +### Subscriptions + +The `subscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_subscribe` and params `[subscriptionType: String, {...params: Array}]` and **MUST** return a Promise that resolves with `subscriptionId: String` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. + +The `unsubscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_unsubscribe` and params `[subscriptionId: String]` and **MUST** return a Promise that resolves with `result: Boolean` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. + +If the `unsubscribe` method returns successfully with a `True` result, the implementing provider **MUST** remove all listeners on the `subscriptionId` using `ethereum.removeAllListeners(subscriptionId);`. + +If an error occurs during processing of the subscription, such as an HTTP error or internal parsing error then the Promise **MUST** return with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. + +The implementing Ethereum Provider **MUST** emit every subscription response `result` with the eventName `subscriptionId`. + +If an error occurs during the listening of the subscription, the Ethereum Provider **MUST** emit an Error object to the eventName `subscriptionId` containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. + +If the implementing provider does not support subscriptions, then it **MUST** leave the `subscribe` and `unsubscribe` methods undefined. + +### Events + +If the network connects, the Ethereum Provider **MUST** emit an event named `connect`. + +If the network connection closes, the Ethereum Provider **MUST** emit an event named `close` with args `code: Number, reason: String` using the [status codes for `CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes) and a short human readable reason. + +If the network the Ethereum Provider is connected to changes, the Ethereum Provider **MUST** emit an event named `networkChanged` with args `networkId: String` containing the ID of the new network (using the Ethereum JSON-RPC call `net_version`). + +If the accounts connected to the Ethereum Provider change, the Ethereum Provider **MUST** send an event with the name `accountsChanged` with args `accounts: Array` containing the accounts' public key(s). + +### Class + +The name of the constructor of the Ethereum Provider **MUST** be `EthereumProvider`. + +## Topics + +### Multiple chain support + +As per discussion in [ethereum/interfaces#16](https://github.com/ethereum/interfaces/issues/16), to handle support of changing networks we recommend introducing a new RPC method `eth_changeNetwork`. In the future depending on the implementation of sharding, an additional method could be `eth_changeShard`. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 0a7466b234e15b37a2e79b02c2b38a667574491e Mon Sep 17 00:00:00 2001 From: achon22 Date: Tue, 17 Jul 2018 07:00:03 -0400 Subject: [PATCH 040/177] added sample implementation and updated functionality (#1216) * eip-1169 * added standard * Update and rename eip-1169.md to eip-1179.md * Update eip-1179.md * name change * Update eip-1178.md * Update eip-1178.md * included implementation * added dex functionality in readme --- EIPS/eip-1178.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-1178.md b/EIPS/eip-1178.md index b609cc12..017feeba 100644 --- a/EIPS/eip-1178.md +++ b/EIPS/eip-1178.md @@ -110,6 +110,22 @@ This method MUST transfer ownership to `_to` or `throw`, no other outcomes can b A conforming contract MUST allow the current owner to "transfer" a token to themselves, as a way of affirming ownership in the event stream. (i.e. it is valid for `_to == msg.sender` if `balanceOf(msg.sender, _classId) >= balance`.) This "no-op transfer" MUST be considered a successful transfer, and therefore MUST fire a `Transfer` event (with the same address for `_from` and `_to`). +## Advanced Ownership and Exchange +```solidity +function approveForToken(uint256 classIdHeld, uint256 quantityHeld, uint256 classIdWanted, uint256 quantityWanted) +``` +Allows holder of one token to allow another individual (or the smart contract itself) to approve the exchange of their tokens of one class for tokens of another class at their specified exchange rate (see sample implementation for more details). This is equivalent to posting a bid in a marketplace. + +```solidity +function exchange(address to, uint256 classIdPosted, uint256 quantityPosted, uint256 classIdWanted, uint256 quantityWanted) +``` +Allows an individual to fill an existing bid (see above function) and complete the exchange of their tokens of one class for another. In the sample implementation, this function call should fail unless the callee has already approved the contract to transfer their tokens. Of course, it is possible to create an implementation where calling this function implicitly assumes approval and the transfer is completed in one step. + +```solidity +transferFrom(address from, address to, uint256 classId) +``` +Allows a third party to initiate a transfer of tokens from `from` to `to` assuming the approvals have been granted. + ## Events **Transfer** @@ -141,7 +157,7 @@ Adoption of the MCFT standard proposal would not pose backwards compatibility is ## Implementation -Coming soon. +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/). From 12072384a9ce97338a1fb1c8cbddcc2b35c6e073 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Tue, 17 Jul 2018 19:01:40 +0800 Subject: [PATCH 041/177] Add EIP-1207 DAuth Access Delegation Standard (#1208) * Add EIP-1207 * Fix eip-1207 title --- EIPS/eip-1207.md | 169 ++++++++++++++++++++++++++++++++++ assets/eip-1207/rationale.png | Bin 0 -> 59478 bytes 2 files changed, 169 insertions(+) create mode 100644 EIPS/eip-1207.md create mode 100644 assets/eip-1207/rationale.png diff --git a/EIPS/eip-1207.md b/EIPS/eip-1207.md new file mode 100644 index 00000000..5f769b75 --- /dev/null +++ b/EIPS/eip-1207.md @@ -0,0 +1,169 @@ +--- +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 +type: Standards Track +category: ERC +created: 2018-07-10 +--- + +DAuth Access Delegation Standard +===== + +## Simple Summary +DAuth is a standard interface for accessing authorization delegation between smart contracts and users. + +## Abstract +The DAuth protocol defines a set of standard API allowing identity delegations between smart contracts without the user's private key. Identity delegations include accessing and operating a user's data and assets contained in the delegated contracts. + +## Motivation +The inspiration for designing DAuth comes from OAuth protocol that is extensively used in web applications. But unlike the centralized authorization of OAuth, DAuth works in a distributed manner, thus providing much more reliability and generality. + +## Specification +![Rationale](../assets/eip-1207/rationale.png) + +**Resource owner**: the authorizer + +**Resource contract**: the contract providing data and operators + +**API**: the resource contract APIs that the grantee contract can invoke + +**Client contract**: the grantee contract using authorization to access and operate the data + +**Grantee request**: the client contract calls the resource contract with the authorizer authorization + + +**AuthInfo** +``` js +struct AuthInfo { + string[] funcNames; + uint expireAt; +} +``` +Required - The struct contains user authorization information +* `funcNames`: a list of function names callable by the granted contract +* `expireAt`: the authorization expire timestamp in seconds + +**userAuth** +``` js +mapping(address => mapping(address => AuthInfo)) userAuth; +``` +Required - userAuth maps (authorizer address, grantee contract address) pair to the user’s authorization AuthInfo object + +**callableFuncNames** +``` js +string[] callableFuncNames; +``` +Required - All methods that are allowed other contracts to call +* The callable function MUST verify the grantee’s authorization + +**updateCallableFuncNames** +``` js +function updateCallableFuncNames(string _invokes) public returns (bool success); +``` +Optional - Update the callable function list for the client contract by the resource contract's administrator +* `_invokes`: the invoke methods that the client contract can call +* return: Whether the callableFuncNames is updated or not +* This method MUST return success or throw, no other outcomes can be possible + +**verify** +``` js +function verify(address _authorizer, string _invoke) internal returns (bool success); +``` +Required - check the invoke method authority for the client contract +* `_authorizer`: the user address that the client contract agents +* `_invoke`: the invoke method that the client contract wants to call +* return: Whether the grantee request is authorized or not +* This method MUST return success or throw, no other outcomes can be possible + +**grant** +``` js +function grant(address _grantee, string _invokes, uint _expireAt) public returns (bool success); +``` +Required - delegate a client contract to access the user's resource +* `_grantee`: the client contract address +* `_invokes`: the callable methods that the client contract can access. It is a string which contains all function names split by spaces +* `_expireAt`: the authorization expire timestamp in seconds +* return: Whether the grant is successful or not +* This method MUST return success or throw, no other outcomes can be possible +* A successful grant MUST fire the Grant event(defined below) + +**regrant** +``` js +function regrant(address _grantee, string _invokes, uint _expireAt) public returns (bool success); +``` +Optional - alter a client contract's delegation + +**revoke** +``` js +function revoke(address _grantee) public returns (bool success); +``` +Required - delete a client contract's delegation +* `_grantee`: the client contract address +* return: Whether the revoke is successful or not +* A successful revoke MUST fire the Revoke event(defined below). + +**Grant** +``` js +event Grant(address _authorizer, address _grantee, string _invokes, uint _expireAt); +``` +* This event MUST trigger when the authorizer grant a new authorization when `grant` or `regrant` processes successfully + +**Revoke** +``` js +event Revoke(address _authorizer, address _grantee); +``` +* This event MUST trigger when the authorizer revoke a specific authorization successfully + +**Callable Resource Contract Functions** + +All public or external functions that are allowed the grantee to call MUST use overload to implement two functions: The First one is the standard method that the user invokes directly, the second one is the grantee methods of the same function name with one more authorizer address parameter. + +Example: +``` js +function approve(address _spender, uint256 _value) public returns (bool success) { + return _approve(msg.sender, _spender, _value); +} + +function approve(address _spender, uint256 _value, address _authorizer) public returns (bool success) { + verify(_authorizer, "approve"); + + return _approve(_authorizer, _spender, _value); +} + +function _approve(address sender, address _spender, uint256 _value) internal returns (bool success) { + allowed[sender][_spender] = _value; + emit Approval(sender, _spender, _value); + return true; +} +``` + +## Rationale + +**Current Limitations** + +The current design of many smart contracts only considers the user invokes the smart contract functions by themselves using the private key. However, in some case, the user wants to delegate other client smart contracts to access and operate their data or assets in the resource smart contract. There isn’t a common protocol to provide a standard delegation approach. + +**Rationale** + +On the Ethereum platform, all storage is transparent and the `msg.sender` is reliable. Therefore, the DAuth don't need an `access_token` like OAuth. DAuth just recodes the users' authorization for the specific client smart contract's address. It is simple and reliable on the Ethereum platform. + +## Backwards Compatibility +This EIP introduces no backward compatibility issues. In the future, the new version protocol has to keep these interfaces. + +## Implementation +Following is the DAuth Interface implementation. Furthermore, the example implementations of EIP20 Interface and ERC-DAuth Interface are also provided. Developers can easily implement their own contracts with ERC-DAuth Interface and other EIP. + +* ERC-DAuth Interface implementation is available at: + + https://github.com/DIA-Network/ERC-DAuth/blob/master/ERC-DAuth-Interface.sol + +* Example implementation with EIP20 Interface and ERC-DAuth Interface is available at: + + https://github.com/DIA-Network/ERC-DAuth/blob/master/eip20-dauth-example/EIP20DAuth.sol + + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/assets/eip-1207/rationale.png b/assets/eip-1207/rationale.png new file mode 100644 index 0000000000000000000000000000000000000000..1f698d8a1bed99508c6daa308b69b592d02b597c GIT binary patch literal 59478 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
  • N%Pn$Z-ZfD73AaCIe$VZN+MyWbt=#g*;NAT^ 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 Date: Tue, 17 Jul 2018 19:10:07 +0800 Subject: [PATCH 042/177] EIP 1203 (#1204) --- EIPS/eip-1203.md | 230 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 EIPS/eip-1203.md diff --git a/EIPS/eip-1203.md b/EIPS/eip-1203.md new file mode 100644 index 00000000..d4e30c9e --- /dev/null +++ b/EIPS/eip-1203.md @@ -0,0 +1,230 @@ +--- +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 +type: Standards Track +category: ERC +created: 2018-07-01 +--- + +## Simple Summary + +A standard interface for multi-class tokens (MCTs). + +## Abstract + +The following standard allows for the implementation of a standard API for MCTs within smart contracts. This standard provides basic functionality to track, transfer, and convert MCTs. + +## Motivation + +This standard is heavily inspired by ERC-20 Token Standard and ERC-721 Non-Fungible Token Standard. However, whereas these standards are chiefly concerned with representation of items/value in a single class, fungible or note, this proposed standard focus on that of a more complexed, multi-class system. It is fair to think of MCTs as a hybrid of fungible tokens (FT) and non-fungible tokens (NFTs), that is tokens are fungible within the same class but non-fungible with that from a different class. And conversions between classes may be optionally supported. + +MCTs are useful in representing various structures with heterogeneous components, such as: + +- **Abstract Concepts:** A company may have different classes of stocks (e.g. senior preferred, junior preferred, class A common, class B common) that together make up its outstanding equities. A shareholder's position of such company composites of zero or more shares in each class. + +- **Virtual Items:** A sandbox computer game may have many types of resources (e.g. rock, wood, berries, cows, meat, knife, etc.) that together make up that virtual world. A player's inventory has any combination and quantity of these resources + +- **Physical Items:** A supermarket may have many SKUs it has available for purchase (e.g. eggs, milk, beef jerky, beer, etc.). Things get added or removed from a shopper's cart as it moves down the aisle. + +It's sometimes possible, especially with regard to abstract concepts or virtual items, to convert from one class to another, at a specified conversion ratio. When it comes to physical items, such conversion essentially is the implementation of bartering. Though it might generally be easier to introduce a common intermediary class, i.e. money. + +## Specification + +```solidity +contract ERC20 { + function totalSupply() public view returns (uint256); + function balanceOf(address _owner) public view returns (uint256); + function transfer(address _to, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +} + +contract ERC1203 is ERC20 { + function totalSupply(uint256 _class) public view returns (uint256); + function balanceOf(address _owner, uint256 _class) public view returns (uint256); + function transfer(address _to, uint256 _class, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _class, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender, uint256 _class) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _class, uint256 _value) public returns (bool); + + function fullyDilutedTotalSupply() public view returns (uint256); + function fullyDilutedBalanceOf(address _owner) public view returns (uint256); + function fullyDilutedAllowance(address _owner, address _spender) public view returns (uint256); + function convert(uint256 _fromClass, uint256 _toClass, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _class, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _class, uint256 _value); + event Convert(uint256 indexed _fromClass, uint256 indexed _toClass, uint256 _value); +} +``` + +### ERC-20 Methods and Events (fully compatible) + +Please see [ERC-20 Token Standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) for detailed specifications. Do note that these methods and events only work on the "default" class of an MCT. + +```solidity + function totalSupply() public view returns (uint256); + function balanceOf(address _owner) public view returns (uint256); + function transfer(address _to, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +``` + +### Tracking and Transferring + +**totalSupply** + +Returns the total number of tokens in the specified `_class` + +```solidity + function totalSupply(uint256 _class) public view returns (uint256); +``` + +**balanceOf** + +Returns the number of tokens of a specified `_class` that the `_owner` has + +```solidity + function balanceOf(address _owner, uint256 _class) public view returns (uint256); +``` + +**transfer** + +Transfer `_value` tokens of `_class` to address specified by `_to`, return `true` if successful + +```solidity + function transfer(address _to, uint256 _class, uint256 _value) public returns (bool); +``` + +**approve** + +Grant `_spender` the right to transfer `_value` tokens of `_class`, return `true` if successful + +```solidity + function approve(address _spender, uint256 _class, uint256 _value) public returns (bool); +``` + +**allowance** + +Return the number of tokens of `_class` that `_spender` is authorized to transfer on the behalf of `_owner` + +```solidity + function allowance(address _owner, address _spender, uint256 _class) public view returns (uint256); +``` + +**transferFrom** + +Transfer `_value` tokens of `_class` from address specified by `_from` to address specified by `_to` as previously approved, return `true` if successful + +```solidity + function transferFrom(address _from, address _to, uint256 _class, uint256 _value) public returns (bool); +``` + +**Transfer** + +Triggered when tokens are transferred or created, including zero value transfers + +```solidity + event Transfer(address indexed _from, address indexed _to, uint256 _class, uint256 _value); +``` + +**Approval** + +Triggered on successful `approve` + +```solidity + event Approval(address indexed _owner, address indexed _spender, uint256 _class, uint256 _value); +``` + +### Conversion and Dilution + +**fullyDilutedTotalSupply** + +Return the total token supply as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedTotalSupply() public view returns (uint256); +``` + +**fullyDilutedBalanceOf** + +Return the total token owned by `_owner` as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedBalanceOf(address _owner) public view returns (uint256); +``` + +**fullyDilutedAllowance** + +Return the total token `_spender` is authorized to transfer on behalf of `_owner` as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedAllowance(address _owner, address _spender) public view returns (uint256); +``` + +**convert** + +Convert `_value` of `_fromClass` to `_toClass`, return `true` if successful + +```solidity + function convert(uint256 _fromClass, uint256 _toClass, uint256 _value) public returns (bool); +``` + +**Conversion** + +Triggered on successful `convert` + +```solidity + event Conversion(uint256 indexed _fromClass, uint256 indexed _toClass, uint256 _value); +``` + +## Rationale +This standard purposely extends ERC-20 Token Standard so that new MCTs following or existing ERC-20 tokens extending this standard are fully compatible with current wallets and exchanges. In addition, new methods and events are kept as closely to ERC-20 conventions as possible for ease of adoption. + +We have considered alternative implementations to support the multi-class structure, as discussed below, and we found current token standards incapable or inefficient in deal with such structures. + +**Using multiple ERC-20 tokens** + +It is certainly possible to create an ERC-20 token for each class, and a separate contract to coordinate potential conversions, but the short coming in this approach is clearly evident. The rationale behind this standard is to have a single contract to manage multiple classes of tokens. + +**Shoehorning ERC-721 token** + +Treating each token as unique, the non-fungible token standard offers maximum representational flexibility arguably at the expense of convenience. The main challenge of using ERC-721 to represent multi-class token is that separate logic is required to keep track of which tokens belongs to which class, a hacky and unnecessary endeavor. + +**Using ERC-1178 token** + +We came across ERC-1178 as we were putting final touches on our own proposal. The two ERCs look very similar on the surface but we believe there're a few key advantages this one has over ERC-1178. + +- ERC-1178 offers no backward compatibility whereas this proposal is an extension of ERC-20 and therefore fully compatible with all existing wallets and exchanges +- By the same token, existing ERC-20 contracts can extend themselves to adopt this standard and support additional classes without affecting their current behaviors +- This proposal introduces the concept of cross class conversion and dilution, making each token class integral part of a whole system rather than many silos + +## Backwards Compatibility +This EIP is fully compatible with the mandatory methods of ERC20 Token Standard so long as the implementation includes a "lowest common denominator" class, which may be class B common/gold coin/money in the abstract/virtual/physical examples above respectively. Where it is not possible to implement such class, then the implementation should specify a default class for tracking or transferring unless otherwise specified, e.g. US dollar is transferred unless other currency is explicitly specified. + +We find it contrived to require the optional methods of ERC20 Token Standard, `name()`, `symbol()`, and `decimals()`, but developers are certainly free to implement these as they wish. + +## Test Cases +The repository at [jeffishjeff/ERC-1203](https://github.com/jeffishjeff/ERC-1203) contains the [sample test cases](https://github.com/jeffishjeff/ERC-1203/blob/master/token.test.js). + +## Implementation +The repository at [jeffishjeff/ERC-1203](https://github.com/jeffishjeff/ERC-1203) contains the [sample implementation](https://github.com/jeffishjeff/ERC-1203/blob/master/token.sol). + +## References +- ERC-20 Token Standard. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md +- ERC-721 Non-Fungible Token Standard. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md +- ERC-1178 Multi-class Token Standard. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1178.md + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 4746d22b07d68693486350b88aaffc8221a82a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 18 Jul 2018 14:04:42 +0200 Subject: [PATCH 043/177] Automatically merged updates to draft EIP(s) 1052 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1052.md | 49 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-1052.md b/EIPS/eip-1052.md index 112a9f73..45c2979e 100644 --- a/EIPS/eip-1052.md +++ b/EIPS/eip-1052.md @@ -1,7 +1,7 @@ --- eip: 1052 title: EXTCODEHASH opcode -author: Nick Johnson +author: Nick Johnson , Paweł Bylica discussions-to: https://ethereum-magicians.org/t/extcodehash-opcode/262 status: Draft type: Standards Track @@ -18,16 +18,57 @@ Many contracts need to perform checks on a contract's bytecode, but do not neces Contracts can presently do this using the `EXTCODECOPY` opcode, but this is expensive, especially for large contracts, in cases where only the hash is required. As a result, we propose a new opcode, `EXTCODEHASH`, which returns the keccak256 hash of a contract's bytecode. ## Specification -A new opcode, `EXTCODEHASH`, is introduced, with number 0x3D. `EXTCODEHASH` takes one argument from the stack, and pushes the keccak256 hash of the code at that address to the stack. + +A new opcode, `EXTCODEHASH`, is introduced, with number 0x3D. The `EXTCODEHASH` +takes one argument from the stack, zeros the first 96 bits +and pushes to the stack the keccak256 hash of the code of the account +at the address being the remaining 160 bits. + +In case the account does not exist `0` is pushed to the stack. + +In case the account does not have code the keccak256 hash of empty data +(i.e. `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`) +is pushed to the stack. + +The gas cost of the `EXTCODEHASH` is 400. + ## Rationale -As described in the motivation section, this opcode is widely useful, and saves on wasted gas in many cases. + +As described in the motivation section, this opcode is widely useful, and saves +on wasted gas in many cases. + +The gas cost is the same as the gas cost for the `BALANCE` opcode because the +execution of the `EXTCODEHASH` requires the same account lookup as in `BALANCE`. + +Only the 20 last bytes of the argument are significant (the first 12 bytes are +ignored) similarly to the semantics of the `BALANCE`, `EXTCODESIZE` and +`EXTCODECOPY`. + +The `EXTCODEHASH` distincts accounts without code and non-existing accounts. +This is consistent with the way accounts are represented in the state trie. +This also allows smart contracts to check whenever an account exists. + ## Backwards Compatibility + There are no backwards compatibility concerns. + ## Test Cases -TBD + +1. The `EXTCODEHASH` of the account without code is `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` + what is the keccack256 hash of empty data. +2. The `EXTCODEHASH` of non-existent account is `0`. +3. The `EXTCODEHASH` of an precompiled contract is either `c5d246...` or `0`. +4. If `EXTCODEHASH` of `A` is `X`, then `EXTCODEHASH` of `A + 2**160` is `X`. +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. +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. + ## Implementation TBD From 5f68f4d49c7a779b1cf64cbb85eeb67ca1847ba2 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Wed, 18 Jul 2018 10:00:36 -0400 Subject: [PATCH 044/177] Automatically merged updates to draft EIP(s) 712 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-712.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-712.md b/EIPS/eip-712.md index eb42486b..f9cdb936 100644 --- a/EIPS/eip-712.md +++ b/EIPS/eip-712.md @@ -215,6 +215,9 @@ Typed data is a JSON object containing type information, domain seprator paramet properties: { types: { type: 'object', + properties: { + EIP712Domain: {type: 'array'}, + }, additionalProperties: { type: 'array', items: { @@ -225,12 +228,14 @@ Typed data is a JSON object containing type information, domain seprator paramet }, required: ['name', 'type'] } - } + }, + required: ['EIP712Domain'] }, primaryType: {type: 'string'}, domain: {type: 'object'}, message: {type: 'object'} - } + }, + required: ['types', 'primaryType', 'domain', 'message'] } ``` From deeb2dbffb939c2680655c7d27c9f7f620e15886 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Wed, 18 Jul 2018 22:00:05 +0200 Subject: [PATCH 045/177] Automatically merged updates to draft EIP(s) 820 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-820.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/EIPS/eip-820.md b/EIPS/eip-820.md index 047822dc..f00841ab 100644 --- a/EIPS/eip-820.md +++ b/EIPS/eip-820.md @@ -41,7 +41,7 @@ This standard also solves the problem of having different addresses for differen ### The smart contract ```solidity -pragma solidity 0.4.20; +pragma solidity 0.4.24; interface ERC820ImplementerInterface { /// @notice Contracts that implement an interferce in behalf of another contract must return true @@ -57,17 +57,10 @@ contract ERC820Registry { bytes4 constant ERC165ID = 0x01ffc9a7; bytes32 constant ERC820_ACCEPT_MAGIC = keccak256("ERC820_ACCEPT_MAGIC"); - mapping (address => mapping(bytes32 => address)) interfaces; mapping (address => address) managers; mapping (address => mapping(bytes4 => bool)) erc165Cache; - modifier canManage(address addr) { - require(getManager(addr) == msg.sender); - _; - } - - event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer); event ManagerChanged(address indexed addr, address indexed newManager); @@ -89,21 +82,25 @@ contract ERC820Registry { /// @notice Sets an external `manager` that will be able to call `setInterfaceImplementer()` /// on behalf of the address. - /// @param addr Address that you are defining the manager for. + /// @param _addr Address that you are defining the manager for. (0x0 if is msg.sender) /// @param newManager The address of the manager for the `addr` that will replace /// the old one. Set to 0x0 if you want to remove the manager. - function setManager(address addr, address newManager) public canManage(addr) { + function setManager(address _addr, address newManager) public { + address addr = _addr == 0 ? msg.sender : _addr; + require(getManager(addr) == msg.sender); managers[addr] = newManager == addr ? 0 : newManager; ManagerChanged(addr, newManager); } /// @notice Query if an address implements an interface and thru which contract - /// @param addr Address that is being queried for the implementation of an interface + /// @param _addr Address that is being queried for the implementation of an interface + /// (if _addr == 0 them `msg.sender` is assumed) /// @param iHash SHA3 of the name of the interface as a string /// Example `web3.utils.sha3('ERC777Token`')` /// @return The address of the contract that implements a specific interface /// or 0x0 if `addr` does not implement this interface - function getInterfaceImplementer(address addr, bytes32 iHash) constant public returns (address) { + function getInterfaceImplementer(address _addr, bytes32 iHash) constant public returns (address) { + address addr = _addr == 0 ? msg.sender : _addr; if (isERC165Interface(iHash)) { bytes4 i165Hash = bytes4(iHash); return erc165InterfaceSupported(addr, i165Hash) ? addr : 0; @@ -112,11 +109,16 @@ contract ERC820Registry { } /// @notice Sets the contract that will handle a specific interface; only - /// the address itself or a `manager` defined for that address can set it - /// @param addr Address that you want to define the interface for + /// a `manager` defined for that address can set it. + /// ( Each address is the manager for itself until a new manager is defined) + /// @param _addr Address that you want to define the interface for + /// (if _addr == 0 them `msg.sender` is assumed) /// @param iHash SHA3 of the name of the interface as a string /// For example `web3.utils.sha3('Ierc777')` for the Ierc777 - function setInterfaceImplementer(address addr, bytes32 iHash, address implementer) public canManage(addr) { + function setInterfaceImplementer(address _addr, bytes32 iHash, address implementer) public { + address addr = _addr == 0 ? msg.sender : _addr; + require(getManager(addr) == msg.sender); + require(!isERC165Interface(iHash)); if ((implementer != 0) && (implementer!=msg.sender)) { require(ERC820ImplementerInterface(implementer).canImplementInterfaceForAddress(addr, iHash) @@ -192,7 +194,7 @@ contract ERC820Registry { ### Raw transaction for deploying the smart contract on any chain ``` -0xf908778085174876e800830c35008080b908246060604052341561000f57600080fd5b6108068061001e6000396000f30060606040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bd578063571a1f66146100f85780635df8122f1461012457806365ba36c11461014957806390e47957146101ac578063aabbb8ca146101ec578063ddc23ddd1461020e575b600080fd5b341561009d57600080fd5b6100bb600160a060020a03600435811690602435906044351661023a565b005b34156100c857600080fd5b6100dc600160a060020a03600435166103ec565b604051600160a060020a03909116815260200160405180910390f35b341561010357600080fd5b6100bb600160a060020a0360043516600160e060020a031960243516610438565b341561012f57600080fd5b6100bb600160a060020a03600435811690602435166104c2565b341561015457600080fd5b61019a60046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061057d95505050505050565b60405190815260200160405180910390f35b34156101b757600080fd5b6101d8600160a060020a0360043516600160e060020a0319602435166105e2565b604051901515815260200160405180910390f35b34156101f757600080fd5b6100dc600160a060020a0360043516602435610658565b341561021957600080fd5b6101d8600160a060020a0360043516600160e060020a0319602435166106b7565b8233600160a060020a031661024e826103ec565b600160a060020a03161461026157600080fd5b61026a8361076e565b1561027457600080fd5b600160a060020a0382161580159061029e575033600160a060020a031682600160a060020a031614155b15610373576040517f4552433832305f4143434550545f4d41474943000000000000000000000000008152601301604051908190039020600160a060020a03831663f008325086866000604051602001526040517c010000000000000000000000000000000000000000000000000000000063ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b151561034b57600080fd5b6102c65a03f1151561035c57600080fd5b505050604051805191909114905061037357600080fd5b600160a060020a0384811660008181526020818152604080832088845290915290819020805473ffffffffffffffffffffffffffffffffffffffff191693861693841790558591907f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db153905160405180910390a450505050565b600160a060020a038082166000908152600160205260408120549091161515610416575080610433565b50600160a060020a03808216600090815260016020526040902054165b919050565b61044282826106b7565b61044d57600061044f565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b8133600160a060020a03166104d6826103ec565b600160a060020a0316146104e957600080fd5b82600160a060020a031682600160a060020a031614610508578161050b565b60005b600160a060020a0384811660008181526001602052604090819020805473ffffffffffffffffffffffffffffffffffffffff191694841694909417909355908416917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a4350905160405180910390a3505050565b6000816040518082805190602001908083835b602083106105af5780518252601f199092019160209182019101610590565b6001836020036101000a038019825116818451161790925250505091909101925060409150505180910390209050919050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff161515610623576106238383610438565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b6000806106648361076e565b1561068957508161067584826105e2565b610680576000610682565b835b91506106b0565b600160a060020a038085166000908152602081815260408083208784529091529020541691505b5092915050565b600080806106e5857f01ffc9a700000000000000000000000000000000000000000000000000000000610790565b90925090508115806106f5575080155b156107035760009250610766565b61071585600160e060020a0319610790565b909250905081158061072657508015155b156107345760009250610766565b61073e8585610790565b90925090506001821480156107535750806001145b156107615760019250610766565b600092505b505092915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6000807f01ffc9a70000000000000000000000000000000000000000000000000000000060405181815284600482015260208160088389617530fa935080519250505092509290505600a165627a7a72305820b424185958879a1eef1cb7235bfd8ed607a7402b46853860e5343340925f028e00291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +0xf908b78085174876e800830c35008080b90864608060405234801561001057600080fd5b50610844806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf578063571a1f66146100fc5780635df8122f1461012a57806365ba36c11461015157806390e47957146101bc578063aabbb8ca146101fe578063ddc23ddd14610222575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610250565b005b3480156100cb57600080fd5b506100e0600160a060020a0360043516610402565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a0360043516600160e060020a03196024351661044e565b34801561013657600080fd5b506100bd600160a060020a03600435811690602435166104d8565b34801561015d57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526101aa9436949293602493928401919081908401838280828437509497506105a09650505050505050565b60408051918252519081900360200190f35b3480156101c857600080fd5b506101ea600160a060020a0360043516600160e060020a031960243516610604565b604080519115158252519081900360200190f35b34801561020a57600080fd5b506100e0600160a060020a036004351660243561067a565b34801561022e57600080fd5b506101ea600160a060020a0360043516600160e060020a0319602435166106f4565b6000600160a060020a038416156102675783610269565b335b90503361027582610402565b600160a060020a03161461028857600080fd5b610291836107a9565b1561029b57600080fd5b600160a060020a038216158015906102bc5750600160a060020a0382163314155b1561039157604080517f4552433832305f4143434550545f4d4147494300000000000000000000000000815281519081900360130181207ff0083250000000000000000000000000000000000000000000000000000000008252600160a060020a038481166004840152602483018790529251909285169163f00832509160448083019260209291908290030181600087803b15801561035b57600080fd5b505af115801561036f573d6000803e3d6000fd5b505050506040513d602081101561038557600080fd5b50511461039157600080fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03808216600090815260016020526040812054909116151561042c575080610449565b50600160a060020a03808216600090815260016020526040902054165b919050565b61045882826106f4565b610463576000610465565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b6000600160a060020a038316156104ef57826104f1565b335b9050336104fd82610402565b600160a060020a03161461051057600080fd5b80600160a060020a031682600160a060020a03161461052f5781610532565b60005b600160a060020a03828116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519185169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a3505050565b6000816040518082805190602001908083835b602083106105d25780518252601f1990920191602091820191016105b3565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff16151561064557610645838361044e565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b60008080600160a060020a038516156106935784610695565b335b91506106a0846107a9565b156106c55750826106b18282610604565b6106bc5760006106be565b815b92506106ec565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b60008080610722857f01ffc9a7000000000000000000000000000000000000000000000000000000006107cb565b9092509050811580610732575080155b1561074057600092506106ec565b61075285600160e060020a03196107cb565b909250905081158061076357508015155b1561077157600092506106ec565b61077b85856107cb565b90925090506001821480156107905750806001145b1561079e57600192506106ec565b506000949350505050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a72305820ba6e246cbdcaf97334eb3dd1ac11e6490103d5ead3f963b5f312a729e88cf9db00291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ``` You can see the string of `a`s at the end of the transaction. This is the `s` of the signature, meaning that its a deterministic by hand forced signature. @@ -222,7 +224,7 @@ This operation can be done in any chain, guaranteed that the contract address is ### Special registry deployment account ``` -0x91c2b265ece9442ed28e3c4283652b1894dcdabb +0xe515e6d0e6b09a60e3cb50c6a8d51d3009ad18af ``` This account is generated by reverse engineering it from it's signature for the transaction, in this way no one knows the private key, but it is known that it's the valid signer of the deployment transaction. @@ -230,7 +232,7 @@ This account is generated by reverse engineering it from it's signature for the ### Deployed contract ``` -0x991a1bcb077599290d7305493c9a630c20f8b798 +0xbe78655dff872d22b95ae366fb3477d977328ade ``` The contract will have this address for every chain it is deployed to. From 9b3ae42b88d3b438397bfdd512cbada1fb30881c Mon Sep 17 00:00:00 2001 From: Afri Schoedon <5chdn@users.noreply.github.com> Date: Thu, 19 Jul 2018 10:20:42 +0200 Subject: [PATCH 046/177] eip-649: update to correct header format (#1233) --- EIPS/eip-649.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-649.md b/EIPS/eip-649.md index 1156ecf6..3583e11f 100644 --- a/EIPS/eip-649.md +++ b/EIPS/eip-649.md @@ -1,7 +1,8 @@ --- eip: 649 title: Metropolis Difficulty Bomb Delay and Block Reward Reduction -author: Afri Schoedon, Vitalik Buterin +author: Afri Schoedon (@5chdn), Vitalik Buterin (@vbuterin) +discussions-to: https://github.com/ethereum/EIPs/issues/649 type: Standards Track category: Core status: Final From a7e495c2c74082e5253fb5d306f26c3b94a52a30 Mon Sep 17 00:00:00 2001 From: gary rong Date: Thu, 19 Jul 2018 19:24:21 +0800 Subject: [PATCH 047/177] Automatically merged updates to draft EIP(s) 1052 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1052.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1052.md b/EIPS/eip-1052.md index 45c2979e..dec0057d 100644 --- a/EIPS/eip-1052.md +++ b/EIPS/eip-1052.md @@ -19,7 +19,7 @@ Contracts can presently do this using the `EXTCODECOPY` opcode, but this is expe ## Specification -A new opcode, `EXTCODEHASH`, is introduced, with number 0x3D. The `EXTCODEHASH` +A new opcode, `EXTCODEHASH`, is introduced, with number 0x3F. The `EXTCODEHASH` takes one argument from the stack, zeros the first 96 bits and pushes to the stack the keccak256 hash of the code of the account at the address being the remaining 160 bits. From c950f128f85c6c898de6f7108547fe993792c714 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 19 Jul 2018 17:51:09 +0200 Subject: [PATCH 048/177] Automatically merged updates to draft EIP(s) 1087 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1087.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/EIPS/eip-1087.md b/EIPS/eip-1087.md index 2ce72ab3..0f730dc1 100644 --- a/EIPS/eip-1087.md +++ b/EIPS/eip-1087.md @@ -15,9 +15,9 @@ This EIP proposes a change to how gas is charged for EVM SSTORE operations, in o ## Motivation Presently, SSTORE operations are charged as follows: - - 20000 gas to set a slot from 0 to non-0 + - 20,000 gas to set a slot from 0 to non-0 - 5,000 gas for any other change - - A 10000 gas refund when a slot is set from non-0 to 0. Refunds are applied at the end of the transaction. + - A 10,000 gas refund when a slot is set from non-0 to 0. Refunds are applied at the end of the transaction. In situations where a single update is made to a storage value in a transaction, these gas costs have been determined to fairly reflect the resources consumed by the operation. However, this results in excessive gas costs for sequences of operations that make multiple updates. @@ -32,8 +32,9 @@ Addressing this issue would also enable new use-cases that are currently cost-pr ## Specification The following changes are made to the EVM: - - An EVM-global 'dirty map' is maintained, tracking all storage slots in all contracts that have been modified in the current transaction. - - When a storage slot is written to for the first time, the slot is marked as dirty. If the slot was previously set to 0, 20000 gas is deducted; otherwise, 5000 gas is deducted. If the slot was previously set to 0, and is being set to 0, only 200 gas is deducted. + - A 'dirty map' for each transaction is maintained, tracking all storage slots in all contracts that have been modified in the current transaction. The dirty map is scoped in the same manner as updates to storage, meaning that changes to the dirty map in a call that later reverts are not retained. + - When a storage slot is written to with the value it already contains, 200 gas is deducted. + - When a storage slot's value is changed for the first time, the slot is marked as dirty. If the slot was previously set to 0, 20000 gas is deducted; otherwise, 5000 gas is deducted. - When a storage slot that is already in the dirty map is written to, 200 gas is deducted. - At the end of the transaction, for each slot in the dirty map: - If the slot was 0 before the transaction and is 0 now, refund 19800 gas. @@ -59,7 +60,19 @@ This EIP requires a hard fork to implement. No contract should see an increase in gas cost for this change, and many will see decreased gas consumption, so no contract-layer backwards compatibility issues are anticipated. ## Test Cases -TBD + + - Writing x to a storage slot that contains 0, where x != 0 (20k gas, no refund) + - Writing y to a storage slot that contained x, where x != y and x != 0 (5k gas, no refund) + - Writing 0 to a storage slot that contains x, where x != 0 (5k gas, 10k refund) + - Writing 0 to a storage slot that already contains zero (200 gas, no refund) + - Writing x to a storage slot that already contains x, where x != 0 (200 gas, no refund) + - Writing x, then y to a storage slot that contains 0, where x != y (20200 gas, no refund) + - Writing x, then y to a storage slot that contains 0, where x != y != z and x != 0 (5200 gas, no refund) + - Writing x, then 0 to a storage slot that contains 0, where x != 0 (20200 gas, 19800 refund) + - Writing x, then y to a storage slot that contains y, where x != y != 0 (5200 gas, 4800 refund) + - Writing x, then 0 to a storage slot that contains 0, then reverting the stack frame in which the writes occurred (20200 gas, no refund) + - Writing x, then y to a storage slot that contains y, then reverting the stack frame in which the writes occurred (5200 gas, no refund) + - In a nested frame, writing x to a storage slot that contains 0, then returning, and writing 0 to that slot (20200 gas, 19800 refund) ## Implementation TBD From a18c57c2ec6bc25ad586c332fba47c65ed1dad62 Mon Sep 17 00:00:00 2001 From: SmeargleUsedFly <39971709+SmeargleUsedFly@users.noreply.github.com> Date: Thu, 19 Jul 2018 11:55:42 -0400 Subject: [PATCH 049/177] EIP-1227: Defuse Difficulty Bomb and Reset Block Reward (#1235) * Add EIP draft. * Rename file. * Update eip-1235.md * Rename eip-1235.md to eip-1227.md * Remove superflous motivation, make it more concrete. --- EIPS/eip-1227.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 EIPS/eip-1227.md diff --git a/EIPS/eip-1227.md b/EIPS/eip-1227.md new file mode 100644 index 00000000..6bc6da90 --- /dev/null +++ b/EIPS/eip-1227.md @@ -0,0 +1,64 @@ +--- +eip: 1227 +title: Defuse Difficulty Bomb and Reset Block Reward +author: SmeargleUsedFly (@SmeargleUsedFly) +discussions-to: https://github.com/ethereum/EIPs/issues/1227 +status: Draft +type: Standards Track +category: Core +created: 2018-07-18 +requires: 649 +--- + +## Simple Summary +This EIP proposes to permanently disable the "difficulty bomb" and reset the block reward to pre-Byzantium levels. + +## Abstract +Starting with `FORK_BLKNUM` the client will calculate the difficulty without the additional exponential component. Furthermore, block rewards will be adjusted to a base of 5 ETH, uncle and nephew rewards will be adjusted accordingly. + +## Motivation +Due to the "difficulty bomb" (also known as the "ice age"), introduced in EIP [#2](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md), an artificial exponential increase in difficulty until chain freeze, users may find it much more challenging to remain on the unforked chain after a hard-fork. This is a desirable effect of the ice age (in fact, its only stated purpose) in the case of a scheduled network upgrade, but is especially problematic when a hard-fork includes a controversial change. + +This situation has already been observed: during the Byzantium hard-fork users were given the "choice" of following the upgraded side of the chain or remaining on the original chain, the latter already experiencing block times greater than 30 seconds. In reality one will find that organizing a disperse and decentralized set of individuals to keep the original, soon-to-be-dead chain alive under such conditions impossible. This is exacerbated when a controversial change, such as EIP [#649](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-649.md), is merged in so close to the hard-fork date, as users cannot be organized to take an educated stance for or against the change on such short notice. + +Ultimately, the difficulty bomb serves but a single purpose: make it more difficult to keep the original chain alive after a hard-fork. This is unacceptable if the only way the community can make their voice heard is running/not running client software, and not through the EIP process, since they effectively have no choice and therefore no power. This EIP proposes to completely eliminate the difficulty bomb, returning some measure of power over Ethereum's governance process to the users, to the community. + +Given the controversy surrounding the directly relevant EIP [#649](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-649.md), the issuance should also be reset to pre-Byzantium levels. It may be reduced again at a later time via a new hard-fork, only this time users would actually have a meaningful choice in accepting the change or not. Note: the issuance reduction is not the focus of this proposal, and is optional; the defusing of the difficulty bomb is of primary concern. + +## Specification +#### Remove Exponential Component of Difficulty Adjustment +For the purposes of `calc_difficulty`, simply remove the exponential difficulty adjustment component, `epsilon`, i.e. the `int(2**((block.number // 100000) - 2))`. + +#### Reset Block, Uncle, and Nephew rewards +To ensure a constant Ether issuance, adjust the block reward to `new_block_reward`, where + + new_block_reward = 5_000_000_000_000_000_000 if block.number >= FORK_BLKNUM else block.reward + +(5E18 wei, or 5,000,000,000,000,000,000 wei, or 5 ETH). + +Analogue, if an uncle is included in a block for `block.number >= FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (8 - k) * new_block_reward / 8 + +This is the existing pre-Byzantium formula for uncle rewards, simply adjusted with `new_block_reward`. + +The nephew reward for `block.number >= FORK_BLKNUM` is + + new_nephew_reward = new_block_reward / 32 + +This is the existing pre-Byzantium formula for nephew rewards, simply adjusted with `new_block_reward`. + +## Rationale +This will permanently, without further changes, disable the "ice age." It will also reset the block reward to pre-Byzantium levels. Both of these changes are specified similarly to EIP [#649](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-649.md), so they should require only minimal changes from client developers. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. However, it may be controversial in nature among different sections of the userbase—the very problem this EIP is made to address. Therefore, it should not be included in a scheduled hardfork at a certain block number. It is suggested to implement this EIP in an isolated hard-fork before the second of the two Metropolis hard-forks. + +## Test Cases +Forthcoming. + +## Implementation +Forthcoming. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 861cec4369979db1829ad5c554ae5f31f82ab9e1 Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Fri, 20 Jul 2018 12:11:59 +0200 Subject: [PATCH 050/177] Automatically merged updates to draft EIP(s) 777 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-777.md | 717 ++++++++++++++++++++++++++++++------------------ 1 file changed, 449 insertions(+), 268 deletions(-) diff --git a/EIPS/eip-777.md b/EIPS/eip-777.md index 6f7b3082..08bedd57 100644 --- a/EIPS/eip-777.md +++ b/EIPS/eip-777.md @@ -14,25 +14,27 @@ requires: 820 This EIP defines a standard interface for token contracts. -*The repository containing the reference implementation for this standard can be found at [jacquesd/ERC777](https://github.com/jacquesd/ERC777) and installed via npm with: `npm install erc777`.* +*The repository containing the reference implementation for this standard can be found at [jacquesd/ERC777] and installed via npm with: `npm install erc777`.* ## Abstract -This standard defines a new way to interact with a Token Contract. +This standard defines a new way to interact with a token contract while remaining backward compatible with [ERC20]. -It defines operators to send tokens on behalf of another address – contract or regular account. It takes advantage of [ERC820](https://eips.ethereum.org/EIPS/eip-820) to find out whether and where to notify contracts and regular addresses when they receive tokens as well as to allow compatibility with old contracts. +It defines advanced features to interact with tokens. Namely, *operators* to send tokens on behalf of another address—contract or regular account—and send/receive *hooks* to offer token holders more control over their tokens. + +It takes advantage of [ERC820] to find out whether and where to notify contracts and regular addresses when they receive tokens as well as to allow compatibility with already-deployed contracts. ## Motivation -This standard tries to improve the widely used [ERC20](https://eips.ethereum.org/EIPS/eip-20) token standard. The main advantages of this standard are: +This standard tries to improve the widely used [ERC20] token standard. The main advantages of this standard are: 1. Uses the same philosophy as Ether in that tokens are sent with `send(dest, value, data)`. -2. Both contracts and regular address can control and reject which token they send by registering a `tokensToSend` function – rejection is done by throwing in the function. -3. Both contracts and regular addresses can control and reject which token they receive by registering a `tokensReceived` function – rejection is done by throwing in the function. -4. The `tokensReceived` function also avoids the double call needed in the ERC20 standard (`approve` / `transferFrom`). +2. Both contracts and regular address can control and reject which token they send by registering a `tokensToSend` hook—rejection is done by throwing in the hook function. +3. Both contracts and regular addresses can control and reject which token they receive by registering a `tokensReceived` hook—rejection is done by throwing in the hook function. +4. The `tokensReceived` hook also avoids the double call needed in the [ERC20] standard (`approve` / `transferFrom`) to send tokens to a contract. 5. The token holder can "authorize" and "revoke" operators who can send tokens on their behalf. These operators are intended to be verified contracts such as an exchange, a cheque processor or an automatic charging system. -6. Every token transaction contains a `userData` bytes field and a similar `operatorData` – in case of an operator transaction – to be used freely by the user (token holder) and the operator respectively to pass data to the recipient. -7. It is backward compatible with wallets that do not contain the `tokensReceived` function by deploying a proxy contract for the wallet. +6. Every token transaction contains a `data` bytes field and a similar `operatorData`—in case of an operator transaction—to be used freely by the token holder and the operator respectively to pass data to the recipient. +7. It is backward compatible with wallets that do not contain the `tokensReceived` hook function by deploying a proxy contract implementing the `tokensReceived` hook for the wallet. ## Specification @@ -40,332 +42,450 @@ This standard tries to improve the widely used [ERC20](https://eips.ethereum.org ``` solidity interface ERC777Token { - function name() public constant returns (string); - function symbol() public constant returns (string); - function totalSupply() public constant returns (uint256); - function granularity() public constant returns (uint256); - function balanceOf(address owner) public constant returns (uint256); - - function send(address to, uint256 amount, bytes userData) public; + function name() public view returns (string); + function symbol() public view returns (string); + function totalSupply() public view returns (uint256); + function balanceOf(address owner) public view returns (uint256); + function granularity() public view returns (uint256); + function defaultOperators() public view returns (address[]); function authorizeOperator(address operator) public; function revokeOperator(address operator) public; - function isOperatorFor(address operator, address tokenHolder) public constant returns (bool); - function operatorSend(address from, address to, uint256 amount, bytes userData, bytes operatorData) public; + function isOperatorFor(address operator, address tokenHolder) public view returns (bool); + + function send(address to, uint256 amount, bytes data) public; + function operatorSend(address from, address to, uint256 amount, bytes data, bytes operatorData) public; + + function burn(uint256 amount, bytes data) public; + function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) public; event Sent( address indexed operator, address indexed from, address indexed to, uint256 amount, - bytes userData, + bytes data, bytes operatorData ); event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData); - event Burned(address indexed operator, address indexed from, uint256 amount, bytes userData, bytes operatorData); + event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); event AuthorizedOperator(address indexed operator, address indexed tokenHolder); event RevokedOperator(address indexed operator, address indexed tokenHolder); } ``` +The token-contract MUST implement the above interface. The implementation MUST follow the specifications described below. -The token-contract MUST register the `ERC777Token` interface with its own address via ERC820. If the contract has a switch to enable or disable ERC777 methods, every time the switch is triggered, the token MUST register or unregister its own address accordingly the `ERC777Token` interface via ERC820. (Unregistering implies setting the address to `0x0`.) +The token-contract MUST register the `ERC777Token` interface with its own address via [ERC820]. If the contract has a switch to enable or disable [ERC777] functions, every time the switch is triggered, the token MUST register or unregister the `ERC777Token` interface for its own address accordingly via [ERC820]. (Unregistering implies setting the address to `0x0`.) -The basic unit token MUST be 1018. +The basic unit token MUST be 1018 (i.e. [ERC20]'s `decimals` MUST be `18`). All functions MUST consider any amount of tokens (such as balances and amount to send, mint, or burn) in the basic unit. -#### Methods -##### name +#### **View Functions** + +The `view` functions detailed below MUST be implemented. + +**`name` function** ``` solidity - function name() public constant returns (string) + function name() public view returns (string) ``` -Returns the name of the token – e.g. `"MyToken"`. +Returns the name of the token—e.g. `"MyToken"`. -> **returns:** Name of the token +> **returns:** Name of the token -
    - -##### symbol +**`symbol` function** ``` solidity -function symbol() public constant returns (string) +function symbol() public view returns (string) ``` -Returns the symbol of the token – e.g. `"MYT"`. +Returns the symbol of the token—e.g. `"MYT"`. -> **returns:** Symbol of the token +> **returns:** Symbol of the token -
    - -##### granularity +**`totalSupply` function** ``` solidity -function granularity() public constant returns (uint256) -``` - -Returns the smallest part of the token that's not divisible. - -Any minting, transfer or burning of tokens MUST be a multiple of this value. Any operation that would result in a balance that's not a multiple of this value, MUST be considered invalid and the transaction MUST throw. - -Most of the tokens SHOULD be fully partitionable, i.e. this function SHOULD return `1` unless there is a good reason for not allowing any partition of the token. - -*NOTE*: `granularity` MUST be greater or equal to `1`. - -> **returns:** The smallest non-divisible part of the token. - -
    - -##### totalSupply - -``` solidity -function totalSupply() public constant returns (uint256) +function totalSupply() public view returns (uint256) ``` Get the total number of minted tokens. -> **returns:** Total supply of tokens currently in circulation. +The total supply MUST be equal to the sum of the balances as returned by `balanceOf` for all the addresses which appeared at least once in any of the `Sent`, `Minted` and `Burned` events. -
    +> **returns:** Total supply of tokens currently in circulation. -##### balanceOf +**`balanceOf` function** ``` solidity -function balanceOf(address tokenHolder) public constant returns (uint256) +function balanceOf(address tokenHolder) public view returns (uint256) ``` Get the balance of the account with address `tokenHolder`. -> **parameters** -> - `tokenHolder`: Address for which the balance is returned + +The balance MUST be zero (`0`) or greater. + +> **parameters** +> `tokenHolder`: Address for which the balance is returned > -> **returns:** Amount of token held by `tokenHolder` in the token contract. +> **returns:** Amount of token held by `tokenHolder` in the token contract. -
    - -##### send +**`granularity` function** ``` solidity -function send(address to, uint256 amount, bytes userData) public +function granularity() public view returns (uint256) ``` -Send `amount` of tokens to address `to`. +Get the smallest part of the token that's not divisible. -This call MUST: - - call the `tokensToSend` method on the contract implementing `ERC777TokensSender` as returned by an ERC820 lookup on the `from` address – regardless if `from` is a regular address or a contract. - - call the `tokensReceived` method on the address implementing `ERC777TokensRecipient` as returned by an ERC820 lookup on the `to` address – regardless if `to` is a regular address or a contract. - - fire the `Sent` event. +The following rules MUST be applied with respect to the *granularity*: -If `to` is a contract which is not prepared to receive tokens. Specifically, it is a contract which does not register an address (its own or another) via ERC820 implementing the `ERC777TokensRecipient` interface. Then `send` MUST throw. +- The *granularity* value MUST NOT be changed. +- The *granularity* value MUST be greater or equal to `1`. +- Any minting, send or burning of tokens MUST be a multiple of the *granularity* value. +- Any operation that would result in a balance that's not a multiple of the *granularity* value, MUST be considered invalid and the transaction MUST throw. -The function MUST `throw` if: - - `msg.sender` account balance does not have enough tokens to spend - - `to` is a contract which is not prepared to receive tokens. +*NOTE*: Most of the tokens SHOULD be fully partitionable, i.e. this function SHOULD return `1` unless there is a good reason for not allowing any partition of the token. -*NOTE*: Sending an amount of `0` is valid and MUST be treated as a regular send. -> **parameters** -> - `to`: tokens recipient -> - `amount`: number of tokens transferred -> - `userData`: information attached to the transaction by the user (sender) +> **returns:** The smallest non-divisible part of the token. -
    +*NOTE*: [`defaultOperators`][defaultOperators] and [`isOperatorFor`][isOperatorFor] are also `view` functions. They are defined under the [operators] for consistency. -##### authorizeOperator +*[ERC20] compatibility requirement*: +The decimals of the token MUST always be `18`. For a *pure* ERC777 token the [ERC20] `decimal` function is OPTIONAL and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of `18` is implied.) For an [ERC20] enabled token, the `decimal` function is REQUIRED and MUST return `18`. + +*[ERC20] compatibility requirement*: +The `name`, `symbol`, `totalSupply`, and `balanceOf` `view` functions MUST be backward compatible with [ERC20]. + +#### **Operators** + +An `operator` is an address which is allowed to send and burn tokens on behalf of another address. + +When an address becomes an *operator* for a *token holder*, an `AuthorizedOperator` event MUST be fired. The `AuthorizedOperator`'s `operator` (topic 1) and `tokenHolder` (topic 2) MUST be the addresses of the *operator* and the *token holder* respectively. + +When the *operator* of a *token holder* is revoked, a `RevokedOperator` event MUST be fired. The `RevokedOperator`'s `operator` (topic 1) and `tokenHolder` (topic 2) MUST be the addresses of the *operator* and the *token holder* respectively. + +*NOTE*: A *token holder* CAN have multiple *operators* at the same time. + +In addition, the token MAY define *default operators*. A *default operator* is an *operator* which is implicitly authorized for all *token holders*. `AuthorizedOperator` events MUST NOT be fired when defining the *default operators*. The rules below apply to *default operators*: + +- The token contract MUST define *default operators* at creation time. +- The *default operators* MUST be invariants. I.e. the token contract MUST NOT add or remove *default operators* ever. +- `AuthorizedOperator` events MUST NOT be fired when defining *default operators*. +- A *token holder* MUST be allowed revoke a *default operator* (unless the *token holder* is a *default operator*). +- A *token holder* MUST be allowed to re-authorize a previously revoked *default operator* +- When a *default operator* is explicitly authorized or revoked for a specific *token holder*, an `AuthorizedOperator` or `RevokedOperator` event (respectively) MUST be fire. + +The following rules apply to any *operator*: + +- An address MUST always be an *operator* for itself. Hence an address MUST NOT ever be revoked as its own *operator*. +- If an address is an *operator* for a token holder `isOperatorFor` MUST return `true`. +- If an address is not an *operator* for a token holder `isOperatorFor` MUST return `false`. + +The `defaultOperators`, `authorizeOperator`, `revokeOperator` and `isOperatorFor` functions described below MUST be implemented to manage *operators*. +Token contracts MAY implement other functions to manage *operators*. + +**`defaultOperators` function**
    + +``` solidity +function defaultOperators() public view returns (address[]) +``` + +Get the list of *default operators* as defined by the token contract + +> **returns:** List of addresses of all the *default operators* + +**`authorizeOperator` function** ``` solidity function authorizeOperator(address operator) public ``` -Authorize a third party `operator` to send tokens on behalf of `msg.sender`. +Set a third party `operator` address as an *operator* of `msg.sender` to send, burn, and mint tokens on its behalf. An `AuthorizedOperator` event MUST be fired on a successful call to this function. -*NOTE*: The token holder (`msg.sender`) is always an operator for himself. That is, he CAN call `operatorSend` on himself. This right cannot be revoked. Therefore if this function is called to set the token holder (`msg.sender`) as operator, then the function MUST throw. +*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to set the token holder (`msg.sender`) as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`). -*NOTE*: A token holder CAN authorize multiple operators at the same time. +*NOTE*: A token holder CAN authorize multiple *operators* at the same time. -> **parameters** -> - `operator`: Address which will be granted rights to manage the tokens. +> **parameters** +> `operator`: Address to set as an *operator* for `msg.sender`. -
    -##### revokeOperator +**`revokeOperator` function** ``` solidity function revokeOperator(address operator) public ``` +Remove the right of `operator` address to be an *operator* for `msg.sender` and to send and burn tokens on its behalf. + Revoke a third party `operator`'s rights to send tokens on behalf of `msg.sender`. A `RevokedOperator` event MUST be fired on a successful call to this function. -*NOTE*: The token holder (`msg.sender`) is always an operator for himself. That is, he CAN call `operatorSend` on himself. This right cannot be revoked. Therefore if this function is called to set the token holder (`msg.sender`) as operator, then the function MUST throw. +*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to revoke the token holder (`msg.sender`) as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`). -> **parameters** -> - `operator`: Address which whose rights to manage the tokens will be revoked. +> **parameters** +> `operator`: Address to rescind as an *operator* for `msg.sender`. -
    -##### isOperatorFor +**`isOperatorFor` function** ``` solidity -function isOperatorFor(address operator, address tokenHolder) public constant returns (bool) +function isOperatorFor(address operator, address tokenHolder) public view returns (bool) ``` -Check whether the `operator` address is allowed to send the tokens held by the `tokenHolder` address. +Indicate whether the `operator` address is an *operator* of the `tokenHolder` address. -> **parameters** -> - `operator`: address to check if it has the right to manage the tokens. -> - `tokenHolder`: address which holds the tokens to be managed. +> **parameters** +> `operator`: Address which may be an *operator* of `tokenHolder`. +> `tokenHolder`: Address of a token holder which may have the `operator` address as an `operator`. +> +> **returns:** `true` if `operator` is an *operator* of `tokenHolder` and `false` otherwise. -
    +*NOTE*: To know which addresses are *operators* for a given *token holder*, one MUST call `isOperatorFor` with the *token holder* for each *default operator* and parse the `AuthorizedOperator`, and `RevokedOperator` events for the *token holder* in question. -##### operatorSend +#### **Sending Tokens** + +When an *operator* sends an `amount` of tokens from a *token holder* to a *recipient* with the associated `data` and `operatorData`, the token contract MUST apply the following rules: + +- Tokens MAY be sent from any *token holder* to any *recipient*. +- The balance of the *token holder* MUST be decreased by the `amount`. +- The balance of the *recipient* MUST be increased by the `amount`. +- The balance of the *token holder* MUST be greater or equal to the `amount`—such that its resulting balance is less than zero after the send. +- The token contract MUST fire a `Sent` event with the correct values as defined in the [`Sent` Event][sent]. +- The *token holder* MAY communicate any data in the `data`. +- The *operator* MAY communicate any data in the `operatorData`. +- The contract MAY modify the `data` and `operatorData` +- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* implements the `ERC777TokensRecipient` interface via [ERC820]. +- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* implements the `ERC777TokensRecipient` interface via [ERC820]. +- When calling `tokensToSend` and/or `tokensReceived`: + - `operator` MUST be the address which initiated the send. (Generally the `msg.sender` of the send function call.) + - `from` MUST be the address of the *token holder*. + - `to` MUST be the address of the *recipient*. + - `data` MUST be the data provided by the *token holder* with the OPTIONAL modifications by the token contract. + - `operatorData` MUST be the data provided by the *operator* with the OPTIONAL modifications by the token contract. + +The token contract MUST throw when sending in any of the following cases: + + - The *operator* address is not an authorized operator for the *token holder* + - The resulting *token holder* and/or *recipient* balances after the send have a granularity smaller than the *granularity* defined by the token contract. + - The address of the *token holder* and/or the *recipient* are `0x0`. + +The token contract MAY send tokens from many *token holders* and/or to many *recipients*. In this case: + +- The previous send rules MUST apply to all the *token holders* and all the *recipients* +- The sum of all the balances incremented MUST be equal to the total sent `amount`. +- The sum of all the balances decremented MUST be equal to the total sent `amount`. +- A `Sent` event MUST be fired for every *token holder* and *recipient* pair with the corresponding amount for each pair. +- The sum of all the amounts from the `Sent` event MUST be equal to the total sent `amount`. + +*NOTE*: Mechanisms such as applying a fee on a send is considered as a send to multiple *recipients*: the intended *recipient* and the fee *recipient*. + +*Implementation Requirement*: +- The token contract MUST call the `tokensToSend` hook *before* updating the state. +- The token contract MUST call the `tokensReceived` hook *after* updating the state. +I.e. `tokensToSend` MUST be called first, then the balances MUST be updated to reflect the send and finally `tokensReceived` MUST be called *afterward*. This means that a `balanceOf` call within `tokensToSend` returns the balance of the address *before* the send and a `balanceOf` call within `tokensReceived` returns the balance of the address *after* the send. + + +*NOTE:* `data` and `operatorData` are fields of free bytes provided by the *token holder* and the *operator* respectively. `data` is intended for the recipient. Typically, the recipient SHOULD indicate which data it expects to receive. (Similarly to the data field when sending ether). `operatorData` is intended more for logging purposes and to very specific cases. (Examples include payment references, cheque numbers, counter signatures and more.) In most of the cases the recipient will just ignore the `operatorData` or at most it will log the `operatorData`. + + +**`Sent` event** ``` solidity -function operatorSend(address from, address to, uint256 amount, bytes userData, bytes operatorData) public +event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data, bytes operatorData) ``` -Send `amount` of tokens on behalf of the address `from` to the address `to`. +Indicate a send of `amount` of tokens from the `from` address to the `to` address by the `operator` address. -`msg.sender` MUST be an authorized operator or the owner for the `from` address. +> **parameters** +> `operator`: address which triggered the send +> `from`: token holder +> `to`: token recipient +> `amount`: number of tokens to send +> `data`: information attached to the send by the token holder +> `operatorData`: information attached to the send by the `operator` -This call MUST: - - call the `tokensToSend` method on the contract implementing `ERC777TokensSender` as returned by an ERC820 lookup on the `from` address - - call the `tokensReceived` method on the contract implementing `ERC777TokensRecipient` as returned by an ERC820 lookup on the `to` address - - fire the `Sent` event +The `send` and `operatorSend` functions described below MUST be implemented to send tokens. +Token contracts MAY implement other functions to send tokens. -If `to` is a contract which is not prepared to receive tokens. Specifically, it is a contract which does not register an address (its own or another) via ERC820 implementing the `ERC777TokensRecipient` interface. Then `operatorSend` MUST throw. +*NOTE*: An address MAY send an amount of `0`. This is valid and MUST be treated as a regular send. -The method MUST throw if: - - `from` account balance does not have enough tokens to spend. - - `to` is a contract which does not register an address (its own or another) via ERC820 which implement the `ERC777TokensRecipient` interface. - - `to` is a contract which is not prepared to receive tokens. - - `msg.sender` is not an authorized operator for `from`. - -> **parameters** -> - `from`: token holder (sender) -> - `to`: tokens recipient -> - `amount`: number of tokens transferred -> - `userData`: information attached to the transaction, previously provided by the sender (`from` address). -> - `operatorData`: information attached to the transaction by the operator - -*NOTE*: The operator is free to pass any data via the `operatorData` parameter but the `userData` parameter is reserved for data provided by the user (token holder). The token holder SHOULD provide this data to the operator beforehand. - -
    - -#### Events - -##### Sent +**`send` function** ``` solidity -event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes userData, bytes operatorData) +function send(address to, uint256 amount, bytes data) public ``` -Indicate a transaction of `amount` of tokens from the `from` address to the `to` address. +Send the `amount` of tokens from the address `msg.sender` to the address `to`. -This event MUST be fired on a successful call to `send` and `operatorSend`. +The *operator* and the *token holder* MUST both be the `msg.sender`. -> **parameters** -> - `operator`: address which triggered the transfer, either the token holder for a direct send or an authorized operator for `operatorSend` -> - `from`: token holder -> - `to`: tokens recipient -> - `amount`: number of tokens sent -> - `userData`: information attached to the transaction by the token holder -> - `operatorData`: information attached to the transaction by the operator +> **parameters** +> `to`: token recipient +> `amount`: number of tokens to send +> `data`: information attached to the send by the *token holder* -
    +**`operatorSend` function** -##### Minted +``` solidity +function operatorSend(address from, address to, uint256 amount, bytes data, bytes operatorData) public +``` + +Send the `amount` of tokens on behalf of the address `from` to the address `to`. + +The *operator* MUST be `msg.sender`. + +*Reminder*: If the *operator* address is not an authorized operator of the `from` address the send must throw. + +*NOTE*: The *operator* MAY pass any data via `operatorData`. The *operator* MUST only pass to `data` data given to it by the *token holder*. The token holder MAY provide this data to the *operator* beforehand through another medium. + +*NOTE*: `from` and `msg.sender` MAY be the same address. I.e an address MAY call `operatorSend` for itself. This call MUST be equivalent to `send` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `send` function). + +> **parameters** +> `from`: token holder +> `to`: token recipient +> `amount`: number of tokens to send +> `data`: information attached to the send, previously provided by the *token holder* +> `operatorData`: information attached to the send by the `operator` + +#### **Minting Tokens** + +Minting tokens is the act of producing new tokens. [ERC777] intentionally does not define explicit functions to mint tokens. This intent comes from the wish to not limit the use of the [ERC777] standard as the minting process is generally specific for every token. + +Nonetheless, the rules below MUST be respected when minting for a *recipient*: + +- Tokens MAY be minted for any *recipient* address. +- The total supply MUST be increased by the amount of tokens minted. +- The balance of `0x0` MUST NOT be decreased. +- The balance of the *recipient* MUST be increased by the amount of tokens minted. +- The token contract MUST fire a `Minted` event with the correct values as defined in the [`Minted` Event][minted]. +- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* implements the `ERC777TokensRecipient` interface via [ERC820]. +- When calling `tokensReceived`: + - `operator` MUST be the address which initiated the minting action. (Generally the `msg.sender` of the minting function call.) + - `from` MUST be `0x0`. + - `to` MUST be the address of the *recipient*. + - `data` MUST be empty. + - `operatorData` MUST contain the data with the OPTIONAL modifications by the token contract, provided by the address which initiated the minting action. + +The token contract MUST throw when minting in any of the following cases: + +- The resulting *recipient* balance after the mint has a granularity smaller than the *granularity* defined by the token contract. +- The *recipient* is a contract and it does not implement the `ERC777TokensRecipient` interface via [ERC820]. +- The address of the *recipient* is `0x0`. + +*[ERC20] compatibility requirement*: +While a `Sent` event MUST NOT be fired when minting, if the token contract is [ERC20] backward compatible, a `Transfer` event with the `from` parameter set to `0x0` MUST be fired as defined in the [ERC20] standard. + +The token contract MAY mint tokens for multiple *recipients* at once. In this case: + +- The previous mint rules MUST apply to all the *recipients*. +- The sum of all the balances incremented MUST be equal to the total minted amount. +- A `Minted` event MUST be fired for every *recipient* with the corresponding amount for each *recipient*. +- The sum of all the amounts from the `Minted` event MUST be equal to the total sent `amount`. + +**`Minted` event** ``` solidity event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData) ``` -Indicate the minting of `amount` of tokens to the `to` address. +Indicate the minting of `amount` of tokens to the `to` address by the `operator` address. -This standard does not enforce a specific way to mint tokens as this can be done in various ways depending on the use case of the tokens. +> **parameters** +> `operator`: address which triggered the mint +> `to`: token recipient +> `amount`: number of tokens minted +> `operatorData`: information attached to the minting by the `operator` -However, this event MUST be fired every time tokens are minted and credited to a `to` recipient address. A `Sent` event (even with the `0x0` as `from` address) MUST NOT be fired to indicate minting. +#### **Burning Tokens** -> **parameters** -> - `operator`: address which triggered the minting -> - `to`: tokens recipient -> - `amount`: number of tokens minted -> - `operatorData`: information attached to the minting by the operator +Burning tokens is the act of destroying existing tokens. [ERC777] explicitly defines two functions to burn tokens (`burn` and `operatorBurn`). This makes it easier for wallets and dapps to let *token holders* burn tokens. However, the token contract MAY prevent some or all *token holders* from burning tokens for any reason. The token contract MAY also define other functions to burn tokens. -###### `tokensReceived` for minting +The rules below MUST be respected when burning the tokens of a *token holder*: -Every mint MUST call `tokensReceived` on the address implementing `ERC777TokensRecipient` for the `to` address as returned by an ERC820 lookup. +- Tokens MAY be burned from any *token holder* address. +- The total supply MUST be decreased by the amount of tokens minted. +- The balance of `0x0` MUST NOT be increased. +- The balance of the *token holder* MUST be decreased by amount of tokens burned. +- The token contract MUST fire a `Burned` event with the correct values as defined in the [`Burned` Event][burned]. +- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* implements the `ERC777TokensSender` interface via [ERC820]. +- When calling `tokensToSend`: + - `operator` MUST be the address which initiated the minting action. (Generally the `msg.sender` of the minting function call.) + - `from` MUST be the address of the *token holder*. + - `to` MUST be `0x0`. + - `data` MUST be empty. + - `operatorData` MUST contain data with the OPTIONAL modifications by the token contract, provided by the address which initiated the burning action (i.e. the *operator*) and/or the token contract. -Minting MUST follow the same rules as `send`/`operatorSend` with the exception that `tokensToSend` MUST NOT be called in any case on any address. In addition, if `to` is a contract which is not prepared to receive tokens. Specifically, it is a contract which does not register an address (its own or another) via ERC820 implementing the `ERC777TokensRecipient` interface. Then the minting MUST throw. +The token contract MUST throw when burning in any of the following cases: -The `from` parameter of `tokensReceived` MUST be `0x0`. The operator MUST be `msg.sender`. The `userData` MUST be empty. `operatorData` MAY contain data related to the minting. +- The address initiating the burn of tokens from a *token holder* MUST be an *operator* of the *token holder*. -
    +- The resulting *token holder* balance after the burn has a granularity smaller than the *granularity* defined by the token contract. +- The balance of *token holder* is inferior to the amount of tokens being burnt (i.e. resulting in a negative balance for the *token holder*). +- The address of the *token holder* is `0x0`. -##### Burned +*[ERC20] compatibility requirement*: +While a `Sent` event MUST NOT be fired when burning, if the token contract is [ERC20] enabled, a `Transfer` event with the `to` parameter set to `0x0` MUST be fired as defined in the [ERC20] standard. + +The token contract MAY burn tokens for multiple *token holders* at once. In this case: + +- The previous burn rules MUST apply to each *token holders*. +- The sum of all the balances decremented MUST be equal to the total burned amount. +- A `Burned` event MUST be fired for every *token holder* with the corresponding amount for each *token holder*. +- The sum of all the amounts from the `Burned` event MUST be equal to the total sent `amount` + +**`Burned` event** ``` solidity -event Burned(address indexed operator, address indexed from, uint256 amount, bytes userData, bytes operatorData) +event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData) ``` -Indicate the burning of `amount` of tokens from the `from` address. +The `burn` and `operatorBurn` functions described below MUST be implemented to burn tokens. +Token contracts MAY implement other functions to burn tokens. -This standard does not enforce a specific way to burn tokens as this can be done in various ways depending on the use case of the tokens. +Indicate the burning of `amount` of tokens from the `from` address by the `operator` address. -However, this event MUST be fired every time tokens are burned and taken from a `from` recipient address. A `Sent` event (even with the `0x0` as `to` address) MUST NOT be fired. - -> **parameters** -> - `operator`: address which triggered the minting -> - `from`: token holder -> - `amount`: number of tokens burned -> - `userData`: information attached to the burn by the token holder -> - `operatorData`: information attached to the burn by the operator - -###### `tokensToSend` for burning - -Every burn MUST call `tokensToSend` on the address implementing `ERC777TokensSender` for the `from` address as returned by an ERC820 lookup. - -Burning MUST follow the same rules as `send`/`operatorSend` with the exception that `tokensReceived` MUST NOT be called in any case on any address. But if the `from` address register an address via ERC820 implementing the `ERC777TokensSender` interface, `tokensToSend` MUST be called. - -The `to` parameter of `tokensToSend` MUST be `0x0`. The operator MUST be `msg.sender`. The `userData` MUST be empty. `operatorData` MAY contain data related to the minting. - -
    - -##### AuthorizedOperator +> **parameters** +> `operator`: address which triggered the burn +> `from`: token holder +> `amount`: number of tokens burned +> `data`: information attached to the burn by the token holder +> `operatorData`: information attached to the burn by the `operator` ``` solidity -event AuthorizedOperator(address indexed operator, address indexed tokenHolder) +function burn(uint256 amount, bytes data) public; ``` -Indicate that the `operator` address is allowed to send the token of (i.e. is an operator for) the address `tokenHolder`. +Burn the `amount` of tokens from the address `msg.sender`. -This event MUST be fired on a successful call to `authorizeOperator`. +The *operator* and the *token holder* MUST both be the `msg.sender`. -> **parameters** -> - `operator`: Address which is granted rights to manage the tokens. -> - `tokenHolder`: address which holds the tokens to be managed. +> **parameters** +> `amount`: number of tokens to burn +> `data`: information attached to the transaction by the token holder -
    - -##### RevokedOperator ``` solidity -event RevokedOperator(address indexed operator, address indexed tokenHolder) +function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) public; ``` -Indicate that the `operator` address is denied the rights to send the token of (i.e. is not operator for) the address `tokenHolder`. +Burn the `amount` of tokens on behalf of the address `from`. -This event MUST be fired on a successful call to `revokeOperator`. +The *operator* MUST be `msg.sender`. -> **parameters** -> - `operator`: Address which is denied the rights to manage the tokens. -> - `tokenHolder`: address which holds the tokens to be managed. +*Reminder*: If the *operator* address is not an authorized operator of the `from` address the burn must throw. -
    +*NOTE*: The *operator* MAY pass any data via `operatorData`. The *operator* MUST only pass to `data` data given to it by the *token holder*. The token holder MAY provide this data to the *operator* beforehand through another medium. -### ERC777TokensSender +*NOTE*: `from` and `msg.sender` MAY be the same address. I.e an address MAY call `operatorBurn` for itself. This call MUST be equivalent to `burn` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `burn` function). -Any address (contract or regular account) CAN register a contract (itself or another) implementing the `ERC777TokensSender` interface via the ERC820 registry. +#### **`ERC777TokensSender` And `tokensToSend` Hook** + +The `tokensToSend` hook notifies of any decrement of balance (send and burn) for a given *token holder*. Any address (regular or contract) wishing to be notified of an debit of their tokens MUST register the address of a contract implementing the `ERC777TokensSender` interface below. A regular address can register a different address—the address of a contract— implementing the interface on its behalf. A contract can register either its address or the address of another contract, but said contract MUST implement the interface. ``` solidity interface ERC777TokensSender { @@ -374,132 +494,162 @@ interface ERC777TokensSender { address from, address to, uint value, - bytes userData, + bytes data, bytes operatorData ) public; } ``` -#### Methods - -##### tokensToSend +**`tokensToSend`** ``` solidity -function tokensToSend(address operator, address from, address to, uint value, bytes userData, bytes operatorData) public +function tokensToSend(address operator, address from, address to, uint value, bytes data, bytes operatorData) public ``` -Notify the transmission of `amount` of tokens from the `from` address. +Notify a send or burn (if `to` is `0x0`) of `amount` tokens from the `from` address to the `to` address by the `operator` address. -> **parameters** -> - `operator`: address which triggered the transfer, either sender for a direct send or an authorized operator for `operatorSend` -> - `from`: token holder (sender) -> - `to`: tokens recipient (or `0x` for burning) -> - `amount`: number of tokens sent or burned -> - `userData`: information attached to the transaction by the sender -> - `operatorData`: information attached to the transaction by the operator +> **parameters** +> `operator`: address which triggered the balance decrease (through sending or burning) +> `from`: *token holder* (sender) +> `to`: *token recipient* for a send and `0x` for a burn +> `amount`: number of tokens the *token holder* balance is decreased by +> `data`: extra information provided by the *token holder* with the OPTIONAL modifications by the token contract +> `operatorData`: extra information provided by the address which triggered the balance decrease with the OPTIONAL modifications by the token contract -###### Burning Versus Sending +The following rules apply to the `tokensToSend` hook: -When tokens are sent as a result of burning: - - `to` MUST be `0x0` - - `userData` MUST be empty - - `operator` MUST be the address which initiated the burning - - `operatorData` MAY contain data +- The `tokensToSend` hook MUST be called every time the balance is decremented. +- The `tokensToSend` hook MUST be called *before* the state is update—i.e. *before* the balance is decremented. +- `operator` MUST be the address which triggered the decrease of the balance. +- `from` MUST be the address of the *token holder* whose balance is decreased for a send or burn. +- `from` MUST be `0x0` for a mint. +- `to` MUST be the address of the *recipient* whose balance is increased for a send or mint. +- `to` MUST be `0x0` for a burn. +- `data` MUST contain the extra information provided by the *token holder* (if any) with the OPTIONAL modifications by the token contract for a send. +- `data` MUST be empty for a mint. +- `operatorData` MUST contain the extra information provided by the address which triggered the decrease of the balance (if any) with the OPTIONAL modifications by the token contract. +- A *token holder* SHALL register its `tokensToSend` hook by registering the address implementing the hook via [ERC820] with the interface name `ERC777TokensSender`. +- A *token holder* CAN register any address (itself or any other) as the implementer of the `ERC777TokensSender` interface. +- A *token holder* MUST register at most one `ERC777TokensSender` implementation at any given time. +- The address claiming to implement the `ERC777TokensSender` interface MUST be a contract which implements the interface. +- The *token holder* MAY block a decrease of its balance by throwing. (I.e. reject the withdrawal of tokens from its account.) +- Any `ERC777TokensSender` implementation MAY be used by more than one *token holder*. -When tokens are sent as a result of sending (`send` or `operatorSend`): - - `to` MUST be the address to which the tokens will be sent - - `to` MUST NOT be `0x0` +*NOTE*: An address can (and MUST) register at most one implementation at a given time. Hence the `ERC777TokensSender` MUST expect to be called by different token contracts. The `msg.sender` of the `tokensToSend` call is expected to be the address of the token contract. - If it is a direct `send` (i.e. not an `operatorSend`) the `operator` MUST be the address from which the tokens originate. That is the `from` and `operator` addresses MUST be equals. +*[ERC20] compatibility requirement*: +This hook takes precedence over [ERC20] and MUST be called (if registered) when calling [ERC20]'s `transfer` and `transferFrom` event. When called from a `transfer`, `operator` MUST be the same value as the `from.` When called from a `transferFrom`, `operator` MUST be the address which issued the `transferFrom` call. +#### **`ERC777TokensRecipient` And `tokensReceived` Hook** -### ERC777TokensRecipient - -Any address (contract or regular account) CAN register a contract (itself or another) implementing the `ERC777TokensRecipient` interface via the ERC820 registry. +The `tokensReceived` hook notifies of any increment of the balance (send and mint) for a given *recipient*. Any address (regular or contract) wishing to be notified of a reception of tokens MUST register the address of a contract implementing the `ERC777TokensRecipient` interface below. A regular address can register a different address—the address of a contract— implementing the interface on its behalf. A contract MUST register either its address or the address of another contract, but said contract MUST implement the interface. ``` solidity interface ERC777TokensRecipient { - function tokensReceived(address operator, address from, address to, uint amount, bytes userData, bytes operatorData) public; + function tokensReceived( + address operator, + address from, + address to, + uint amount, + bytes data, + bytes operatorData + ) public; } ``` -#### Methods - -##### tokensReceived +**`tokensReceived`** ``` solidity -function tokensReceived(address operator, address from, address to, uint amount, bytes userData, bytes operatorData) public +function tokensReceived(address operator, address from, address to, uint amount, bytes data, bytes operatorData) public ``` -Notify the transmission of `amount` of tokens to the `to` address. +Notify a send or mint (if `from` is `0x0`) of `amount` tokens from the `from` address to the `to` address by the `operator` address. -> **parameters** -> - `operator`: address which triggered the transfer, either sender for a direct send or an authorized operator for `operatorSend` -> - `from`: token holder (sender or `0x` for minting) -> - `to`: tokens recipient (or `0x` for burning) -> - `amount`: number of tokens sent, minted or burned -> - `userData`: information attached to the transaction by the sender -> - `operatorData`: information attached to the transaction by the operator +> **parameters** +> `operator`: address which triggered the balance increase (through sending or minting) +> `from`: *token holder* for a send and `0x` for a mint +> `to`: *token recipient* +> `amount`: number of tokens the *recipient* balance is increased by +> `data`: extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint, with the OPTIONAL modifications by the token contract +> `operatorData`: extra information provided by the address which triggered the balance increase with the OPTIONAL modifications by the token contract -###### Minting Versus Sending +- The `tokensReceived` hook MUST be called every time the balance is incremented. +- The `tokensReceived` hook MUST be called *after* the state is update—i.e. *after* the balance is incremented. +- `operator` MUST be the address which triggered the increase of the balance. +- `from` MUST be the address of the *token holder* whose balance is decreased for a send or burn. +- `from` MUST be `0x0` for a mint. +- `to` MUST be the address of the *recipient* whose balance is increased for a send or mint. +- `to` MUST be `0x0` for a burn. +- `data` MUST contain the extra information provided by the *token holder* (if any) with the OPTIONAL modifications by the token contract for a send. +- `data` MUST be empty for a mint. +- `operatorData` MUST contain the extra information provided by the address which triggered the increase of the balance (if any) with the OPTIONAL modifications by the token contract. +- A *token holder* SHALL register its `tokensReceived` hook by registering the address implementing the hook via [ERC820] with the interface name `ERC777TokensRecipient`. +- If the *recipient* is a contract, which has not registered an `ERC777TokensRecipient` implementation; the token contract: + - MUST throw if the `tokensReceived` hook is called from a mint or send call. + - SHOULD accept if the `tokensReceived` hook is called from an ERC20 `transfer` or `transferFrom` call. +- A *token holder* CAN register any address (itself or any other) as the implementer of the `ERC777TokensRecipient` interface. +- A *token holder* MUST register at most one `ERC777TokensRecipient` implementation at any given time. +- The address claiming to implement the `ERC777TokensRecipient` interface MUST be a contract which implements the interface. +- The *token holder* MAY block an increase of its balance by throwing. (I.e. reject the reception of tokens.) +- Any `ERC777TokensRecipient` implementation MAY be used by more than one *token holder*. -When tokens are received as a result of minting: - - `from` MUST be `0x0` - - `userData` MUST be empty - - `operator` MUST be the address which initiated the minting - - `operatorData` MAY contain data. +*NOTE*: An address can (and MUST) register at most one implementation at a given time. Hence the `ERC777TokensRecipient` MUST expect to be called by different token contracts. The `msg.sender` of the `tokensReceived` call is expected to be the address of the token contract. -When tokens are received as a result of sending (`send` or `operatorSend`): - - `from` MUST be the one from which the tokens originate and - - `from` MUST NOT be `0x0`. +*[ERC20] compatibility requirement*: +This hook takes precedence over [ERC20] and MUST be called (if registered) when calling [ERC20]'s `transfer` and `transferFrom` event. When called from a `transfer`, `operator` MUST be the same value as the `from.` When called from a `transferFrom`, `operator` MUST be the address which issued the `transferFrom` call. -If it is a direct `send` (i.e. not an `operatorSend`) the `operator` MUST be the address from which the tokens originate. That is the `from` and `operator` addresses MUST be equals. +#### **Note On Gas Consumption** +Dapps and wallets SHOULD first estimate the gas required when sending, minting,or burning tokens—using [`eth_estimateGas`][eth_estimateGas]—to avoid running out of gas during the transaction. ### Logo -| **Image** |  ![beige logo](../assets/eip-777/logo/png/ERC-777-logo-beige-48px.png) |  ![white logo](../assets/eip-777/logo/png/ERC-777-logo-white-48px.png) |  ![light grey logo](../assets/eip-777/logo/png/ERC-777-logo-light_grey-48px.png) |  ![dark grey logo](../assets/eip-777/logo/png/ERC-777-logo-dark_grey-48px.png) |  ![black logo](../assets/eip-777/logo/png/ERC-777-logo-black-48px.png?raw=true) +| **Image** |  ![beige logo] |  ![white logo] |  ![light grey logo] |  ![dark grey logo] |  ![black logo][black logo] | |----------:|:---------:|:---------:|:------------:|:-----------:|:---------:| | **Color** | beige | white | light grey | dark grey | black | | **Hex** | `#C99D66` | `#FFFFFF` | `#EBEFF0` | `#3C3C3D` | `#000000` | -The logo MUST NOT be used to advertise, promote or associate in any way technology – such as tokens – which is not ERC777 compliant. +The logo MAY be used, modified and adapted to promote valid [ERC777] token implementations and [ERC777] compliant technologies such as wallets and dapps. -The logo for the standard can be found in the `/assets/eip-777/logo` folder in `svg` and `png` formats. The `png` version of the logo offers a few sizes in pixels. If needed, other sizes CAN be created by converting from `svg` into `png`. +[ERC777] token contract authors CAN create a specific logo for their token based on this logo. -ERC777 token contract authors CAN create a specific logo for their token based on this logo. +The logo MUST NOT be used to advertise, promote or associate in any way technology—such as tokens—which is not [ERC777] compliant. + +The logo for the standard can be found in the [`/assets/eip-777/logo`][logos] folder in `svg` and `png` formats. The `png` version of the logo offers a few sizes in pixels. If needed, other sizes CAN be created by converting from `svg` into `png`. ## Rationale -This standard solves some of the problems of the [EIP223](https://github.com/ethereum/EIPs/issues/223) and goes a step further by allowing operators (generally contracts) that can manage the tokens in the same way that the ERC20 with infinite `approve` was allowed. +This standard solves some of the shortcomings of [ERC20]. It avoids the problems of the [EIP223]. It is backward compatible with [ERC20]. Lastly and goes a step further by allowing *operators* (generally contracts) which can manage the tokens in the same way that the [ERC20] with infinite `approve` was allowed and hooks to give greater control to *token holders* over their tokens. -Also, the usage of ERC820 allows backward compatibility with wallets and proxy contracts without having to be redeployed. +Also, the usage of [ERC820] allows backward compatibility with wallets and existing contracts without having to be redeployed thanks proxy contracts implementing the hooks. -## Backward Compatibility (ERC20Token) +## Backward Compatibility -This EIP does not introduce backward incompatibilities and is compatible with the older ERC20 token standard. +This EIP does not introduce backward incompatibilities and is backward compatible with the older [ERC20] token standard. -This EIP does not use `transfer` and `transferFrom` and uses `send` and `operatorSend` to avoid mistakes in knowing which interface you are using. +This EIP does not use `transfer` and `transferFrom` and uses `send` and `operatorSend` to avoid confusion and mistakes when deciphering which token standard is being used. -This standard allows the implementation of ERC20 functions `transfer`, `transferFrom`, `approve` and `allowance` alongside to make a token compatible with ERC20. +This standard allows the implementation of [ERC20] functions `transfer`, `transferFrom`, `approve` and `allowance` alongside to make a token fully compatible with [ERC20]. -The token CAN implement `decimals()` for backward compatibility. If implemented, it MUST always return `18`. +The token CAN implement `decimals()` for backward compatibility with [ERC20]. If implemented, it MUST always return `18`. -Therefore a token contract CAN implement both ERC20 and ERC777 in parallel. Read-only functions (such as `name`, `symbol`, `balanceOf`, `totalSupply`) and internal data (such as the mapping of balances) overlap without problems. Note however that the following functions are mandatory in ERC777 and MUST be implemented: `name`, `symbol` `balanceOf` and `totalSupply` (`decimal` is not part of the ERC777 standard). +Therefore a token contract CAN implement both [ERC20] and [ERC777] in parallel. The specification of the `view` functions (such as `name`, `symbol`, `balanceOf`, `totalSupply`) and internal data (such as the mapping of balances) overlap without problems. Note however that the following functions are mandatory in [ERC777] and MUST be implemented: `name`, `symbol` `balanceOf` and `totalSupply` (`decimal` is not part of the [ERC777] standard). -The write methods from both standards are decoupled and can operate independently from each other. Note that ERC20 functions SHOULD be limited to only being called from old contracts. +The state-modifying functions from both standards are decoupled and can operate independently from each other. Note that [ERC20] functions SHOULD be limited to only being called from old contracts. -If the token implements ERC20, it MUST register the `ERC20Token` interface with its own address via ERC820. If the contract has a switch to enable or disable ERC20 methods, every time the switch is triggered, the token MUST register or unregister its own address accordingly the `ERC20Token` interface via ERC820. (Unregistering implies setting the address to `0x0`.) +If the token implements [ERC20], it MUST register the `ERC20Token` interface with its own address via [ERC820]. If the contract has a switch to enable or disable [ERC20] functions, every time the switch is triggered, the token MUST register or unregister its own address accordingly the `ERC20Token` interface via [ERC820]. (Unregistering implies setting the address to `0x0`.) -The only difference for new contracts implementing ERC20 is that registration of `ERC777TokensSender` and `ERC777TokensRecipient` via ERC820 takes precedence over ERC20. This means that even with an ERC20 `transfer` call, the token contract MUST check via ERC820 if the `from` / `to` address implements `tokensToSend` / `tokensReceived` and call it if available. Note that when calling ERC20 `transfer` on a contract, if the contract does not implement `tokensReceived`, the `transfer` call SHOULD still be accepted even if this means the tokens will probably be locked. +The difference for new contracts implementing [ERC20] is that +`tokensToSend` and `tokensReceived` hooks take precedence over [ERC20]. This means that even with an [ERC20] `transfer` and `transferFrom` call, the token contract MUST check via [ERC820] if the `from` and the `to` address implement `tokensToSend` and `tokensReceived` hook respectively. If any hook is implemented, it MUST be called. Note that when calling [ERC20] `transfer` on a contract, if the contract does not implement `tokensReceived`, the `transfer` call SHOULD still be accepted even if this means the tokens will probably be locked. + +The table below summarizes the different actions the token contract MUST take when sending, minting and transferring token via [ERC777] and [ERC20]: -The table below summarizes the different actions the token contract must take when sending, minting and transferring token via ERC777 and ERC20:

  • - + - - + +
    ERC820ERC820 to addressERC777 send/operatorSend and MintingERC20 transferERC777 Sending And MintingERC20 transfer/transferFrom
    @@ -527,16 +677,47 @@ The table below summarizes the different actions the token contract must take wh
    -There is no particular action to take if `tokensToSend` is not implemented. The transfer MUST proceed and be canceled only if another condition is not respected such as lack of funds, a throw in `tokensReceived` (if present) or in some specific cases if `tokensReceived` is not implemented. +There is no particular action to take if `tokensToSend` is not implemented. The transfer MUST proceed and be canceled only if another condition is not respected such as lack of funds or a throw in `tokensReceived` (if present). + +During a send, mint and burn, the respective `Sent`, `Minted` and `Burned` events MUST be fired. In addition, if the token contract declares that it implements `ERC20Token` via [ERC820], the token contract MUST fire a `Transfer` event (as specified in the [ERC20] standard). During an [ERC20]'s `transfer` or `transferFrom` functions, a valid `Sent` event MUST be fired. + +This means that for any movement of tokens, two events MAY be fired: an [ERC20] `Transfer` and an [ERC777] `Sent`, `Minted` or `Burnt` (depending on the type of movement). Third-party developers MUST be careful not to consider both events as separate movements. As a general rule, if the token is considered by an application as an ERC20 token, then only the `Transfer` event MUST be considered. If the token is considered by an application as an ERC777 token, the only the `Sent`, `Minted` and `Burnt` events MUST be considered. ## Test Cases -The [repository with the reference implementation](https://github.com/jacquesd/ERC777) contains all the [tests](https://github.com/jacquesd/ERC777/blob/master/test/ReferenceToken.test.js). +The [repository with the reference implementation][jacquesd/ERC777] contains all the [tests][ref tests]. ## Implementation -The repository at [jacquesd/ERC777](https://github.com/jacquesd/ERC777) contains the [reference implementation](https://github.com/jacquesd/ERC777/blob/master/contracts/examples/ReferenceToken.sol). +The repository at [jacquesd/ERC777] contains the [reference implementation]. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0]. + +[operators]: #operators + + +[ERC20]: https://eips.ethereum.org/EIPS/eip-20 +[ERC777]: https://eips.ethereum.org/EIPS/eip-777 +[ERC820]: https://eips.ethereum.org/EIPS/eip-820 +[jacquesd/ERC777]: https://github.com/jacquesd/ERC777 +[ref tests]: https://github.com/jacquesd/ERC777/blob/master/test/ReferenceToken.test.js +[reference implementation]: https://github.com/jacquesd/ERC777/blob/master/contracts/examples/ReferenceToken.sol +[EIP223]: https://github.com/ethereum/EIPs/issues/223 +[eth_estimateGas]: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_estimategas + +[isOperatorFor]: #isOperatorFor +[defaultOperators]: #defaultOperators +[sent]: #sent +[minted]: #minted +[burned]: #burned + +[logos]: https://github.com/ethereum/EIPs/tree/master/assets/eip-777/logo +[beige logo]: ../assets/eip-777/logo/png/ERC-777-logo-beige-48px.png +[white logo]: ../assets/eip-777/logo/png/ERC-777-logo-white-48px.png +[light grey logo]: ../assets/eip-777/logo/png/ERC-777-logo-light_grey-48px.png +[dark grey logo]: ../assets/eip-777/logo/png/ERC-777-logo-dark_grey-48px.png +[black logo]: ../assets/eip-777/logo/png/ERC-777-logo-black-48px.png + +[CC0]: https://creativecommons.org/publicdomain/zero/1.0/ From 2e9723e7634d6403cb2d5897e5b21bd42f626977 Mon Sep 17 00:00:00 2001 From: Afri Schoedon <5chdn@users.noreply.github.com> Date: Fri, 20 Jul 2018 23:20:20 +0200 Subject: [PATCH 051/177] EIP-1234: Constantinople Difficulty Bomb Delay and Block Reward Adjustment (#1234) * difficulty bomb: duplicate eip 649 * eip-1234: update spec to match with constantinople requirements * eip-1234: update header * Remove reference to #1227 --- EIPS/eip-1234.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 EIPS/eip-1234.md diff --git a/EIPS/eip-1234.md b/EIPS/eip-1234.md new file mode 100644 index 00000000..6136078d --- /dev/null +++ b/EIPS/eip-1234.md @@ -0,0 +1,61 @@ +--- +eip: 1234 +title: Constantinople Difficulty Bomb Delay and Block Reward Adjustment +author: Afri Schoedon (@5chdn) +discussions-to: https://github.com/ethereum/EIPs/pull/1234 +type: Standards Track +category: Core +status: Draft +created: 2018-07-19 +--- + +## Simple Summary +The average block times are increasing due to the difficulty bomb (also known as the "_ice age_") slowly accelerating. This EIP proposes to delay the difficulty bomb for approximately one and a half year and to reduce the block rewards with the Constantinople fork, the second part of the Metropolis fork. + +## Abstract +Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty based on a fake block number suggesting the client that the difficulty bomb is adjusting around 6 million blocks later than previously specified with the Homestead fork. Furthermore, block rewards will be adjusted to a base of 2 ETH, uncle and nephew rewards will be adjusted accordingly. + +## Motivation +The Casper development and switch to proof-of-stake is delayed, the Ethash proof-of-work should be feasible for miners and allow sealing new blocks every 15 seconds on average for another one and a half years. With the delay of the ice age, there is a desire to not suddenly also increase miner rewards. The difficulty bomb has been known about for a long time and now it's going to stop from happening. In order to maintain stability of the system, a block reward reduction that offsets the ice age delay would leave the system in the same general state as before. Reducing the reward also decreases the likelihood of a miner driven chain split as Ethereum approaches proof-of-stake. + +## 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: + + fake_block_number = max(0, block.number - 6_000_000) if block.number >= CNSTNTNPL_FORK_BLKNUM else block.number + +#### Adjust Block, Uncle, and Nephew rewards +To ensure a constant Ether issuance, adjust the block reward to `new_block_reward`, where + + new_block_reward = 2_000_000_000_000_000_000 if block.number >= CNSTNTNPL_FORK_BLKNUM else block.reward + +(2E18 wei, or 2,000,000,000,000,000,000 wei, or 2 ETH). + +Analogue, if an uncle is included in a block for `block.number >= CNSTNTNPL_FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (8 - k) * new_block_reward / 8 + +This is the existing pre-Constantinople formula for uncle rewards, simply adjusted with `new_block_reward`. + +The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is + + new_nephew_reward = new_block_reward / 32 + +This is the existing pre-Constantinople formula for nephew rewards, simply adjusted with `new_block_reward`. + +## Rationale +This will delay the ice age by 42 million seconds (approximately 1.4 years), so the chain would be back at 30 second block times in summer 2020. An alternate proposal was to add special rules to the difficulty calculation to effectively _pause_ the difficulty between different blocks. This would lead to similar results. + +This was previously discussed at All Core Devs Meeting [#42](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2042.md) without stating any details. This draft can be used to discuss further changes to the difficulty bomb and issuance in subsequent meetings. This EIP-1234 opposes directly the intent of [EIP-1227](https://github.com/ethereum/EIPs/issues/1227) which should be also considered in discussions. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, _Constantinople_. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementation +The implementation in it's logic does not differ from [EIP-649](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-649.md) and can be used as temporary reference. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 0b3ca485d35faa09c69de0a95b276f7c19c71d51 Mon Sep 17 00:00:00 2001 From: Zainan Zhou Date: Sat, 21 Jul 2018 10:35:33 +0200 Subject: [PATCH 052/177] PR for EIP-1202 (#1237) * Creating EIP-1202 WIP * Rename eip-1202 to eip-1202.md * Update eip-1202.md Update links * Turns out only 1203 does not exist * Update eip-1202.md * Add copyright waiver * Update eip-1202.md * Update eip-1202.md * Fix build breakage * Update eip-1202.md * Update eip-1202.md --- EIPS/eip-1202.md | 199 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 EIPS/eip-1202.md diff --git a/EIPS/eip-1202.md b/EIPS/eip-1202.md new file mode 100644 index 00000000..ab6b7a43 --- /dev/null +++ b/EIPS/eip-1202.md @@ -0,0 +1,199 @@ +--- +eip: 1202 +title: Voting Standard +author: Zainan Victor Zhou (@xinbenlv), Evan (@evbots) +type: Standards Track +category: ERC +status: Draft +created: 2018-07-08 +discussions-to: https://github.com/ethereum/EIPs/issues/1202 +--- + +Note: we are still open to have co-author to collaborate. + +## Simple Summary +Propose a standard interface for voting. + +## Abstract +This proposal creates a standard API for implementing voting within smart contract. This standard provides basic functionality 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 +1. Allow general UI and applications to be built on top of a standardized voting to allow more general user to participate, and encourage more DApp and DAO to think about their governance +2. Allow delegate voting / smart contract voting, automatic voting +3. Allow voting results to be recorded on-chain, in a standard way, and allow DAOs and DApps to honor the voting result programmatically. +4. Allow the compatibility with token standard such as [ERC-20](https://eips.ethereum.org/EIPS/eip-20) or other new standards([EIP-777](https://eips.ethereum.org/EIPS/eip-777)) and item standard such as [EIP-721](https://eips.ethereum.org/EIPS/eip-721) +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](https://eips.ethereum.org/EIPS/eip-20) approach and later a [EIP-777](https://eips.ethereum.org/EIPS/eip-777) 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: +1. Determine on issuing new token, issuing more token or issuing sub-token +2. Determine on creating new item under [EIP-721](https://eips.ethereum.org/EIPS/eip-721) +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 + +### Simple Version +The simple version of specification makes the assumption that each smart contract voting standard is: *Single Issue*, *Single Selection* and *Single Outcome* + +```solidity +pragma solidity ^0.4.22; + + +/** + * - Single issue + * - Single selection + * + * Discussion: + * 1. Each address has a weight determined by other input decided by the actual implementation + * which is suggested to be set upon the initialization + * 2. Is there certain naming convention to follow? + */ +interface ERC1202 { + + // Vote with an option. The caller needs to handle success or not + function vote(uint option) external returns (bool success); + function setStatus(bool isOpen) external returns (bool success); + + function issueDescription() external view returns (string desc); + function availableOptions() external view returns (uint[] options); + function optionDescription(uint option) external view returns (string desc); + function ballotOf(address addr) external view returns (uint option); + function weightOf(address addr) external view returns (uint weight); + function getStatus() external view returns (bool isOpen); + function weightedVoteCountsOf(uint option) external view returns (uint count); + function winningOption() external view returns (uint option); + + event OnVote(address indexed _from, uint _value); + event OnStatusChange(bool newIsOpen); +} +``` + +### Advanced Version +```solidity +pragma solidity ^0.4.22; + + +/** + * - Multiple issue + * - Multiple selection + * - Ordered multiple result + * Discussion: + * 1. Each address has a weight determined by other input decided by the actual implementation + * which is suggested to be set upon the initialization + * 2. Is there certain naming convention to follow? + */ +contract AdvancedERC1202 { + + // 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); +} +``` + +## Rationale + +We made the following design decisions and here are the rationales. + + - **Granularity and Anonymity:**: We created a `view` function `ballotOf` primarily making it easier for people to check the vote from certain address. This has the following assumptions: + + * 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. + +## Examples +### Example 1: Simplest Version: Single Issue Yes/No Question Per Smart Contract Address Per Non-Weighted Vote + + - [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) + +### 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) + +### 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) + +## Summary of Discussions + +### Early Feedback Questions (2018-07-08) +Here are a few early questions I'd like to ask people here. +1. Have we had any duplicated EIPs that I overlooked. If not, have anyone attempted to do so, and why it did not continue to exist? +**Answer**: We concluded there is no duplicated efforts working on creating a voting standard. + +2. Should each issue have its own smart contract address (like individual item on [EIP-721](https://eips.ethereum.org/EIPS/eip-721)) or should it support multiple items in [EIP-1155](https://eips.ethereum.org/EIPS/eip-1155), or should it support multi-class voting in [EIP-1178](https://eips.ethereum.org/EIPS/eip-1178), [EIP-1203](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1203.md) (e.g. certain issue can override another issue) +**Answer**: We will provide examples of both and seek comments. + +3. Should the voting support proxy(e.g [EIP-897](https://eips.ethereum.org/EIPS/eip-897), [EIP-1167](https://eips.ethereum.org/EIPS/eip-1167)) and migration? What are potential security concerns +**Answer**: It shall not be determined by this ERC. + +4. Should it be proposed in a single phase standard or multiple separate into multiple phase, with earlier phase supporting easiest and simplest interface, and later phase supporting more advanced interfaces? (I intuitively believe it will be the latter, but not sure if it might be possible to do it all-at once.) +**Answer**: It will unavoidably require upgrade in the future, but supporting multiple issue multiple options will be good enough so far. + +1. Should it support or optionally support [EIP-165](https://eips.ethereum.org/EIPS/eip-165)? For public voting, support EIP-165 make it easier to discover, but for secret voting people might not want to disclose a voting for certain issue even exist. +**Answer**: It shall not be determined by this ERC. + +### Second Feedback Questions 2018-07-19 +1. Is it technically possible to achieve anonymous voting on current Ethereum/EVM setup, is it possible that people either hide their identity, or hide what selection they made in a vote given that for a smart contract the public states are visible from block history directly, and internal private state can be replied in any fullnode? + +2. number byte length: for simplicity we are using `uint` anywhere undecided. We need to decided what number byte length should we use for `weights` and `options`. + + +## Bibliography +### Related EIPs + - [EIP-20: ERC-20 Token Standard (a.k.a. ERC-20)](https://eips.ethereum.org/EIPS/eip-20) + - [EIP-165: Standard Interface Detection](https://eips.ethereum.org/EIPS/eip-165) + - [EIP-721: Non-Fungible Token Standard(a.k.a. ERC-721)](https://eips.ethereum.org/EIPS/eip-721) + - [EIP-777: A New Advanced Token Standard](https://eips.ethereum.org/EIPS/eip-777) + - [EIP-897: ERC DelegateProxy](https://eips.ethereum.org/EIPS/eip-897) + - [EIP-1155: Crypto Item Standard](https://eips.ethereum.org/EIPS/eip-1155) + - [EIP-1178: Multi-class Token Standard](https://eips.ethereum.org/EIPS/eip-1178) + - [EIP-1167: Minimal Proxy Contract](https://eips.ethereum.org/EIPS/eip-1167) + - [EIP-1203: Multi-class Token Standard(ERC-20 Extension)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1203.md) + +### Related Projects + - [Ethereum DAO: How to build a DEMOCRACY on the blockchain](https://www.ethereum.org/dao) + - [Carbon Vote](http://carbonvote.com/) + +### Request for Comment +We kindly request the community for comments, in particular, the following ERC and projects related authors: + + - ERC-20: @frozeman, @vbuterin + - ERC-721: @fulldecent, Dieter Shirley, Jacob Evans, Nastassia Sachs + - Carbon Vote: @lgn21st, @Aaaaaashu + +Your comments and suggestions will be greatly appreciated. + +## EIP WIP Work Logs + +- 2018-07-08: (@xinbenlv) Created early feedback request. Asked around discussion channels suggested in [EIP-1](https://eips.ethereum.org/EIPS/eip-1), such as [Ethereum-Magicians](https://ethereum-magicians.org/t/eip-x-voting-standard-early-feedback-wanted/670/2), [Gitter](https://gitter.im/ethereum/EIPs), [Reddit](https://www.reddit.com/r/ethereum/comments/8x6k11/early_feedback_request_for_eipx_voting_standard/) +- 2018-07-09: (@xinbenlv)Added examples layout. Request for co-author. +- 2018-07-17: (@xinbenlv)Added co-author. @evbots, added two simple examples. +- 2018-07-19: (@xinbenlv)Added interface-like specification. Moved content from [issue](https://github.com/ethereum/EIPs/issues/1202) to [xinbenlv's Github repo](https://github.com/xinbenlv/eip-1202-draft/blob/master/EIP-1202.md) . Added TokenVote example. +- 2018-07-20: (@xinbenlv)Added advanced token vote exmaple. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 3dae347d8a4a083ca16b4242864b543d7c5863d5 Mon Sep 17 00:00:00 2001 From: Micah Zoltu Date: Sat, 21 Jul 2018 16:36:20 +0800 Subject: [PATCH 053/177] Creates Remove Difficulty Bomb EIP (#1240) * Creates Remove Difficulty Bomb EIP The difficulty bomb operates under the assumption that miners decide what code economic participants are running, rather than economic participants deciding for themselves. In reality, miners will mine whatever chain is most profitable and the most profitable chain is the one that economic participants use. If 99% of miners mine a chain that no economic participants use then that chain will have no value and the miners will cease mining of it in favor of some other chain that does have economic participants. Another way to put this is that miners will follow economic participants, not the other way around. With the difficulty bomb removed, when Casper is released it will be up to economic participants to decide whether they want the features that Casper enables or not. If they do not want Casper, they are free to continue running unpatched clients and participating in the Ethereum network as it exists today. This freedom of choice is the cornerstone of DLTs and making it hard for people to make that choice (by creating an artificial pressure) does not work towards that goal of freedom of choice. If the development team is not confident that economic participants will want Casper, then they should re-evaluate their priorities rather than trying to force Casper onto users. Personal Note: I think we will see almost all economic participants in Ethereum switch to PoS/Sharding without any extra pressure beyond client defaults. * Added more clarity on how to fix the difficulty. * Adds Yellow Paper implementation. * Sets EIP number. --- EIPS/eip-1240.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 EIPS/eip-1240.md diff --git a/EIPS/eip-1240.md b/EIPS/eip-1240.md new file mode 100644 index 00000000..cb8ff2fa --- /dev/null +++ b/EIPS/eip-1240.md @@ -0,0 +1,40 @@ +--- +eip: 1240 +title: Remove Difficulty Bomb +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/difficulty-bomb-removal/832 +type: Standards Track +category: Core +status: Draft +created: 2018-07-21 +--- + +## Simple Summary +The average block times are increasing due to the difficulty bomb (also known as the "_ice age_") slowly accelerating. This EIP proposes to remove the difficulty bomb with the Constantinople fork, the second part of the Metropolis fork. + +## Abstract +Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty without considering the current block number. + +## Motivation +The difficulty bomb operates under the assumption that miners decide what code economic participants are running, rather than economic participants deciding for themselves. In reality, miners will mine whatever chain is most profitable and the most profitable chain is the one that economic participants use. If 99% of miners mine a chain that no economic participants use then that chain will have no value and the miners will cease mining of it in favor of some other chain that does have economic participants. Another way to put this is that miners will follow economic participants, not the other way around. + +## Specification +#### Remove Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, change the epsilon component to `0` rather than having it be a function of block number. + +## Rationale +With the difficulty bomb removed, when Casper is released it will be up to economic participants to decide whether they want the features that Casper enables or not. If they do not want Casper, they are free to continue running unpatched clients and participating in the Ethereum network as it exists today. This freedom of choice is the cornerstone of DLTs and making it hard for people to make that choice (by creating an artificial pressure) does not work towards that goal of freedom of choice. If the development team is not confident that economic participants will want Casper, then they should re-evaluate their priorities rather than trying to force Casper onto users. + +Author Personal Note: I think we will see almost all economic participants in Ethereum switch to PoS/Sharding without any extra pressure beyond client defaults. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, _Constantinople_. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementations +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/). From 95317d5126bdc832a3558aaafaf448814b9d1529 Mon Sep 17 00:00:00 2001 From: Afri Schoedon <5chdn@users.noreply.github.com> Date: Sat, 21 Jul 2018 13:18:57 +0200 Subject: [PATCH 054/177] Automatically merged updates to draft EIP(s) 1234 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1234.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1234.md b/EIPS/eip-1234.md index 6136078d..9d2f04bb 100644 --- a/EIPS/eip-1234.md +++ b/EIPS/eip-1234.md @@ -2,7 +2,7 @@ eip: 1234 title: Constantinople Difficulty Bomb Delay and Block Reward Adjustment author: Afri Schoedon (@5chdn) -discussions-to: https://github.com/ethereum/EIPs/pull/1234 +discussions-to: https://ethereum-magicians.org/t/eip-1234-constantinople-difficulty-bomb-delay-and-block-reward-adjustment/833 type: Standards Track category: Core status: Draft From 5e71d200dae7d56adaa9e0ca2eda3f5976117104 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Sat, 21 Jul 2018 13:25:54 -0400 Subject: [PATCH 055/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 4a02acc4..62c8adde 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -51,7 +51,7 @@ Dapps MUST request an Ethereum provider API by sending a message using the [`win #### `[2] INJECT` -Ethereum-enabled DOM environments MUST expose an Ethereum provider API as a global `ETHEREUM_PROVIDER` variable on the `window` object. +Ethereum-enabled DOM environments MUST expose an Ethereum provider API as a global `ethereum` variable on the `window` object. #### `[3] NOTIFY` @@ -71,7 +71,7 @@ window.addEventListener('message', function (event) { if (!event.data || !event.data.type) { return; } if (event.data.type === 'ETHEREUM_PROVIDER_SUCCESS') { // Provider API exposed, continue - const networkVersion = await ETHEREUM_PROVIDER.send('net_version', []); + const networkVersion = await ethereum.send('net_version', []); console.log(networkVersion); } }); From 5508f14208339d39082ea3c36b4069488ab85660 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Sun, 22 Jul 2018 10:13:16 -0700 Subject: [PATCH 056/177] Automatically merged updates to draft EIP(s) 1193 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1193.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md index b2e0e1a2..deaf17a0 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -1,6 +1,6 @@ --- eip: 1193 -title: Ethereum Provider API +title: Ethereum Provider JavaScript API author: Ryan Ghods (@ryanio), Marc Garreau (@marcgarreau) discussions-to: https://ethereum-magicians.org/t/eip-1193-ethereum-provider/640 status: Draft @@ -12,7 +12,7 @@ requires: 1102 ## Summary -This proposal formalizes an Ethereum Provider API. +This proposal formalizes an Ethereum Provider JavaScript API. The provider is designed to be minimal, containing 3 methods: `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`. @@ -220,23 +220,23 @@ If the Ethereum JSON-RPC API returns a response object with no error, then the P If the Ethereum JSON-RPC API returns response object that contains an error property then the Promise **MUST** be rejected with an Error object containing the `response.error.message` as the Error message, `response.error.code` as a code property on the error and `response.error.data` as a data property on the error. -If an error occurs during processing, such as an HTTP error or internal parsing error then the Promise **MUST** be rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. +If an error occurs during processing, such as an HTTP error or internal parsing error then the Promise **MUST** be rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. If the implementing Ethereum Provider is not talking to an external Ethereum JSON-RPC API provider then it **MUST** resolve with an object that matches the JSON-RPC API object as specified in the [Ethereum JSON-RPC documentation](https://github.com/ethereum/wiki/wiki/JSON-RPC). In case of an error, ensure that the Promise is rejected with an Error that matches the above shape. ### Subscriptions -The `subscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_subscribe` and params `[subscriptionType: String, {...params: Array}]` and **MUST** return a Promise that resolves with `subscriptionId: String` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. +The `subscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_subscribe` and params `[subscriptionType: String, {...params: Array}]` and **MUST** return a Promise that resolves with `subscriptionId: String` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. -The `unsubscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_unsubscribe` and params `[subscriptionId: String]` and **MUST** return a Promise that resolves with `result: Boolean` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. +The `unsubscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_unsubscribe` and params `[subscriptionId: String]` and **MUST** return a Promise that resolves with `result: Boolean` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. If the `unsubscribe` method returns successfully with a `True` result, the implementing provider **MUST** remove all listeners on the `subscriptionId` using `ethereum.removeAllListeners(subscriptionId);`. -If an error occurs during processing of the subscription, such as an HTTP error or internal parsing error then the Promise **MUST** return with an Error object containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. +If an error occurs during processing of the subscription, such as an HTTP error or internal parsing error then the Promise **MUST** return with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. The implementing Ethereum Provider **MUST** emit every subscription response `result` with the eventName `subscriptionId`. -If an error occurs during the listening of the subscription, the Ethereum Provider **MUST** emit an Error object to the eventName `subscriptionId` containing a human readable string message describing the error and **SHOULD** populate code and data properties on the error object with additional error details. +If an error occurs or the network changes during the listening of the subscription, the Ethereum Provider **MUST** emit an Error object to the eventName `subscriptionId` containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. If the implementing provider does not support subscriptions, then it **MUST** leave the `subscribe` and `unsubscribe` methods undefined. @@ -254,12 +254,6 @@ If the accounts connected to the Ethereum Provider change, the Ethereum Provider The name of the constructor of the Ethereum Provider **MUST** be `EthereumProvider`. -## Topics - -### Multiple chain support - -As per discussion in [ethereum/interfaces#16](https://github.com/ethereum/interfaces/issues/16), to handle support of changing networks we recommend introducing a new RPC method `eth_changeNetwork`. In the future depending on the implementation of sharding, an additional method could be `eth_changeShard`. - ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From e3aa2a01f6791cbc88122414c7c2949384786e86 Mon Sep 17 00:00:00 2001 From: Micah Zoltu Date: Mon, 23 Jul 2018 18:52:47 +0800 Subject: [PATCH 057/177] Automatically merged updates to draft EIP(s) 1240 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1240.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1240.md b/EIPS/eip-1240.md index cb8ff2fa..361a0863 100644 --- a/EIPS/eip-1240.md +++ b/EIPS/eip-1240.md @@ -19,8 +19,8 @@ Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty w The difficulty bomb operates under the assumption that miners decide what code economic participants are running, rather than economic participants deciding for themselves. In reality, miners will mine whatever chain is most profitable and the most profitable chain is the one that economic participants use. If 99% of miners mine a chain that no economic participants use then that chain will have no value and the miners will cease mining of it in favor of some other chain that does have economic participants. Another way to put this is that miners will follow economic participants, not the other way around. ## Specification -#### Remove Difficulty with Fake Block Number -For the purposes of `calc_difficulty`, change the epsilon component to `0` rather than having it be a function of block number. +#### Remove Difficulty +For the purposes of `calc_difficulty`, if `block.number >= CNSTNTNPL_FORK_BLKNUM` then change the epsilon component to `0` rather than having it be a function of block number. ## Rationale With the difficulty bomb removed, when Casper is released it will be up to economic participants to decide whether they want the features that Casper enables or not. If they do not want Casper, they are free to continue running unpatched clients and participating in the Ethereum network as it exists today. This freedom of choice is the cornerstone of DLTs and making it hard for people to make that choice (by creating an artificial pressure) does not work towards that goal of freedom of choice. If the development team is not confident that economic participants will want Casper, then they should re-evaluate their priorities rather than trying to force Casper onto users. From 153bce30913f2d1c0a34780d8201a5f2b7f65177 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Tue, 24 Jul 2018 03:46:20 +0800 Subject: [PATCH 058/177] Correct discussions-to destination (#1099) --- eip-X.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eip-X.md b/eip-X.md index 1b602fe4..e1c1c80b 100644 --- a/eip-X.md +++ b/eip-X.md @@ -2,7 +2,7 @@ eip: title: author: , FirstName (@GitHubUsername) and GitHubUsername (@GitHubUsername)> -discussions-to: +discussions-to: status: Draft type: category (*only required for Standard Track): From a8e18356ea7e9b4a28419dd03e21472b9a5bcec1 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Mon, 23 Jul 2018 13:32:56 -0700 Subject: [PATCH 059/177] Automatically merged updates to draft EIP(s) 1193 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1193.md | 211 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 201 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md index deaf17a0..c78161fc 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -2,7 +2,7 @@ eip: 1193 title: Ethereum Provider JavaScript API author: Ryan Ghods (@ryanio), Marc Garreau (@marcgarreau) -discussions-to: https://ethereum-magicians.org/t/eip-1193-ethereum-provider/640 +discussions-to: https://ethereum-magicians.org/t/eip-1193-ethereum-provider-javascript-api/640 status: Draft type: Standards Track category: Interface @@ -33,7 +33,7 @@ See the [available methods](https://github.com/ethereum/wiki/wiki/JSON-RPC#json- #### Subscribe ```js -ethereum.subscribe(subscriptionType: String, params?: Array): Promise; +ethereum.subscribe(subscriptionType: String, params?: Array): Promise; ``` Promise resolves with `subscriptionId: String` or rejects with `Error`. @@ -220,23 +220,23 @@ If the Ethereum JSON-RPC API returns a response object with no error, then the P If the Ethereum JSON-RPC API returns response object that contains an error property then the Promise **MUST** be rejected with an Error object containing the `response.error.message` as the Error message, `response.error.code` as a code property on the error and `response.error.data` as a data property on the error. -If an error occurs during processing, such as an HTTP error or internal parsing error then the Promise **MUST** be rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. +If an error occurs during processing, such as an HTTP error or internal parsing error, then the Promise **MUST** be rejected with an Error object. -If the implementing Ethereum Provider is not talking to an external Ethereum JSON-RPC API provider then it **MUST** resolve with an object that matches the JSON-RPC API object as specified in the [Ethereum JSON-RPC documentation](https://github.com/ethereum/wiki/wiki/JSON-RPC). In case of an error, ensure that the Promise is rejected with an Error that matches the above shape. +If the implementing Ethereum Provider is not talking to an external Ethereum JSON-RPC API provider then it **MUST** resolve with an object that matches the JSON-RPC API object as specified in the [Ethereum JSON-RPC documentation](https://github.com/ethereum/wiki/wiki/JSON-RPC). ### Subscriptions -The `subscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_subscribe` and params `[subscriptionType: String, {...params: Array}]` and **MUST** return a Promise that resolves with `subscriptionId: String` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. +The `subscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_subscribe` and params `[subscriptionType: String, {...params: Array}]` and **MUST** return a Promise that resolves with `subscriptionId: String` or rejected with an Error object. -The `unsubscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_unsubscribe` and params `[subscriptionId: String]` and **MUST** return a Promise that resolves with `result: Boolean` or rejected with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. +The `unsubscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_unsubscribe` and params `[subscriptionId: String]` and **MUST** return a Promise that resolves with `result: Boolean` or rejected with an Error object. If the `unsubscribe` method returns successfully with a `True` result, the implementing provider **MUST** remove all listeners on the `subscriptionId` using `ethereum.removeAllListeners(subscriptionId);`. -If an error occurs during processing of the subscription, such as an HTTP error or internal parsing error then the Promise **MUST** return with an Error object containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. +If an error occurs during processing of the subscription, such as an HTTP error or internal parsing error then the Promise **MUST** return with an Error object. The implementing Ethereum Provider **MUST** emit every subscription response `result` with the eventName `subscriptionId`. -If an error occurs or the network changes during the listening of the subscription, the Ethereum Provider **MUST** emit an Error object to the eventName `subscriptionId` containing a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. +If an error occurs or the network changes during the listening of the subscription, the Ethereum Provider **MUST** emit an Error object to the eventName `subscriptionId`. If the implementing provider does not support subscriptions, then it **MUST** leave the `subscribe` and `unsubscribe` methods undefined. @@ -244,9 +244,9 @@ If the implementing provider does not support subscriptions, then it **MUST** le If the network connects, the Ethereum Provider **MUST** emit an event named `connect`. -If the network connection closes, the Ethereum Provider **MUST** emit an event named `close` with args `code: Number, reason: String` using the [status codes for `CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes) and a short human readable reason. +If the network connection closes, the Ethereum Provider **MUST** emit an event named `close` with args `code: Number, reason: String` following the [status codes for `CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). -If the network the Ethereum Provider is connected to changes, the Ethereum Provider **MUST** emit an event named `networkChanged` with args `networkId: String` containing the ID of the new network (using the Ethereum JSON-RPC call `net_version`). +If the network the provider is connected to changes, the provider **MUST** emit an event named `networkChanged` with args `networkId: String` containing the ID of the new network (using the Ethereum JSON-RPC call `net_version`). If the accounts connected to the Ethereum Provider change, the Ethereum Provider **MUST** send an event with the name `accountsChanged` with args `accounts: Array` containing the accounts' public key(s). @@ -254,6 +254,197 @@ If the accounts connected to the Ethereum Provider change, the Ethereum Provider The name of the constructor of the Ethereum Provider **MUST** be `EthereumProvider`. +### web3.js Provider + +The implementing Ethereum Provider **MUST** be compatible as a `web3.js` provider. This is accomplished by providing two methods in the `EthereumProvider`: `sendAsync(payload: Object, callback: (error: any, result: any) => void): void` and `isConnected(): Boolean`. + +### Error object and codes + +If an Error object is returned, it **MUST** contain a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. + +Appropriate error codes **SHOULD** follow the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). + +## Example Class Implementation + +```js +class EthereumProvider extends EventEmitter { + constructor() { + // Call super for `this` to be defined + super(); + + // Init storage + this._isConnected = false; + this._nextJsonrpcId = 0; + this._promises = {}; + this._activeSubscriptions = []; + + // Fire the connect + this._connect(); + + // Listen for jsonrpc responses + window.addEventListener('message', this._handleJsonrpcMessage.bind(this)); + } + + /* Methods */ + + send(method, params = []) { + if (!method || typeof method !== 'string') { + return new Error('Method is not a valid string.'); + } + + if (!(params instanceof Array)) { + return new Error('Params is not a valid array.'); + } + + const payload = { + id: this._nextJsonrpcId++, + jsonrpc: '2.0', + method, + params + }; + + const promise = new Promise((resolve, reject) => { + this._promises[payload.id] = { resolve, reject }; + }); + + // Send jsonrpc request to node + window.postMessage({ type: 'write', message: payload }, origin); + + return promise; + } + + subscribe(subscriptionType, params) { + return this.send('eth_subscribe', [subscriptionType, ...params]).then( + subscriptionId => { + this._activeSubscriptions.push(subscriptionId); + } + ); + } + + unsubscribe(subscriptionId) { + return this.send('eth_unsubscribe', [subscriptionId]).then(success => { + if (success) { + // Remove subscription + this._activeSubscription = this._activeSubscription.filter( + id => id !== subscriptionId + ); + // Remove listeners on subscriptionId + this.removeAllListeners(subscriptionId); + } + }); + } + + /* Internal methods */ + + _handleJsonrpcMessage(event) { + // Return if no data to parse + if (!event || !event.data) { + return; + } + + let data; + try { + data = JSON.parse(event.data); + } catch (error) { + // Return if we can't parse a valid object + return; + } + + // Return if not a jsonrpc response + if (!data || !data.message || !data.message.jsonrpc) { + return; + } + + const message = data.message; + const { id, method, error, result } = message; + + if (typeof id !== 'undefined') { + const promise = this._promises[id]; + if (promise) { + // Handle pending promise + if (data.type === 'error') { + promise.reject(message); + } else if (message.error) { + promise.reject(error); + } else { + promise.resolve(result); + } + delete this.promises[id]; + } + } else { + if (method && method.indexOf('_subscription') > -1) { + // Emit subscription result + const { subscription, result } = message.params; + this.emit(subscription, result); + } + } + } + + /* Connection handling */ + + _connect() { + // Send to node + window.postMessage({ type: 'create' }, origin); + + // Reconnect on close + this.once('close', this._connect.bind(this)); + } + + /* Events */ + + _emitConnect() { + this._isConnected = true; + this.emit('connect'); + } + + _emitClose(code, reason) { + this._isConnected = false; + this.emit('close', code, reason); + + // Send Error objects to any open subscriptions + this._activeSubscriptions.forEach(id => { + const error = new Error( + `Provider connection to network closed. + Subscription lost, please subscribe again.` + ); + this.emit(id, error); + }); + // Clear subscriptions + this._activeSubscriptions = []; + } + + _emitNetworkChanged(networkId) { + this.emit('networkChanged', networkId); + } + + _emitAccountsChanged(accounts) { + this.emit('accountsChanged', accounts); + } + + /* web3.js provider compatibility */ + + sendAsync(payload, callback) { + return this.send(payload.method, payload.params) + .then(result => { + const response = payload; + response.result = result; + callback(null, response); + }) + .catch(error => { + callback(error, null); + // eslint-disable-next-line no-console + console.error( + `Error from EthereumProvider sendAsync ${payload}: ${error}` + ); + }); + } + + isConnected() { + return this._isConnected; + } +} +``` + ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 5e0ce971db113a5e6ec17fd3d1f6606300d802ac Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Mon, 23 Jul 2018 19:45:43 -0700 Subject: [PATCH 060/177] Automatically merged updates to draft EIP(s) 1193 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1193.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md index c78161fc..67792ef6 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -12,7 +12,7 @@ requires: 1102 ## Summary -This proposal formalizes an Ethereum Provider JavaScript API. +This EIP formalizes an Ethereum Provider JavaScript API to create consistency across clients. The provider is designed to be minimal, containing 3 methods: `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`. @@ -264,7 +264,7 @@ If an Error object is returned, it **MUST** contain a human readable string mess Appropriate error codes **SHOULD** follow the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). -## Example Class Implementation +## Sample Class Implementation ```js class EthereumProvider extends EventEmitter { @@ -296,19 +296,19 @@ class EthereumProvider extends EventEmitter { return new Error('Params is not a valid array.'); } - const payload = { - id: this._nextJsonrpcId++, - jsonrpc: '2.0', - method, - params - }; + const id = this._nextJsonrpcId++; + const jsonrpc = '2.0'; + const payload = { jsonrpc, id method, params }; const promise = new Promise((resolve, reject) => { this._promises[payload.id] = { resolve, reject }; }); - // Send jsonrpc request to node - window.postMessage({ type: 'write', message: payload }, origin); + // Send jsonrpc request to Mist + window.postMessage( + { type: 'mistAPI_ethereum_provider_write', message: payload }, + origin + ); return promise; } @@ -383,8 +383,8 @@ class EthereumProvider extends EventEmitter { /* Connection handling */ _connect() { - // Send to node - window.postMessage({ type: 'create' }, origin); + // Send to Mist + window.postMessage({ type: 'mistAPI_ethereum_provider_connect' }, origin); // Reconnect on close this.once('close', this._connect.bind(this)); From 106500292d9f5c43f336670cdc100c87703b2580 Mon Sep 17 00:00:00 2001 From: Afri Schoedon <5chdn@users.noreply.github.com> Date: Tue, 24 Jul 2018 10:02:02 +0200 Subject: [PATCH 061/177] Automatically merged updates to draft EIP(s) 1234 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1234.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1234.md b/EIPS/eip-1234.md index 9d2f04bb..7317dc99 100644 --- a/EIPS/eip-1234.md +++ b/EIPS/eip-1234.md @@ -55,7 +55,7 @@ This EIP is not forward compatible and introduces backwards incompatibilities in Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. ## Implementation -The implementation in it's logic does not differ from [EIP-649](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-649.md) and can be used as temporary reference. +The implementation in it's logic does not differ from [EIP-649](https://github.com/ethereum/EIPs/blob/master/EIPS/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/). From 5ca0e7d86d22d2b3e9a535bfa9ed68555f484e52 Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Tue, 24 Jul 2018 08:26:06 -0700 Subject: [PATCH 062/177] Adds EIP 998 (#1252) * Create eip-173.md * Removed "review-period-end" * Update eip-173.md Fixed the notice for the transferOwnership function. Stated that emitting the OwnershipTransferred event on contract creation is unspecified. * Update eip-173.md Updated info about the OwnershipTransferred event. "The OwnershipTransferred event does not have to be emitted when a contract is created." * Update eip-173.md Set the discussions-to data. * Create eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md * Update eip-998.md --- EIPS/eip-998.md | 780 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 780 insertions(+) create mode 100644 EIPS/eip-998.md diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md new file mode 100644 index 00000000..dc99ebc3 --- /dev/null +++ b/EIPS/eip-998.md @@ -0,0 +1,780 @@ +--- +eip: 998 +title: ERC-998 Composable Non-Fungible Token Standard +author: Matt Lockyer , Nick Mudge +discussions-to: https://github.com/ethereum/EIPs/issues/998 +type: Standards Track +category: ERC +status: Draft +created: 2018-07-07 +requires: 721, 165 +--- + +## Simple Summary + +An extension of the [ERC721 standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) to enable ERC721 tokens to own other ERC721 tokens and ERC20 tokens. + +## Abstract + +An ERC988 composable is an ERC721 token with additional functionality for owning or being owned by other ERC721 tokens. An ERC998 composable can also own ERC20 tokens. + +An ERC721 token owns another token if the the ownership of the token has been transferred to it. + +A top-down composable contract stores and keeps track of child tokens for each of its tokens. + +A bottom-up composable contract stores and keeps track of a parent token for each its tokens. + +With either kind of composable it is possible to compose lists or trees of ERC721 tokens connected by ownership. Any such structure will have a single owner address at the root of the structure that is the owner of the entire composition. The entire composition can be transferred with one transaction by changing the root owner. + +Both kinds of composable, top-down and bottom-up, have their advantages and disadvantages which are explained in the Rational section. It is possible for a composable token to be one or both kinds of composable. + +## Specification + +This specification specifies: + +1. [ERC721 top-down composable tokens that receive, hold and transfer ERC721 tokens](#erc721-top-down-composable) +2. [ERC20 top-down composable tokens that receive, hold and transfer ERC20 tokens](#erc20-top-down-composable) +3. [ERC721 bottom-up composable tokens that attach themselves to other ERC721 tokens.](#erc721-bottom-up-composable) + +### ERC721 + +Both ERC721 top-down and ERC721 bottom-up composable contracts must implement the [ERC721 interface](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md). + +### ERC165 + +The [ERC165 standard](https://eips.ethereum.org/EIPS/eip-165) must be applied to each ERC998 interface that is used. + +### Authentication + +Authenticating whether a user or contract can execute some action works the same for both top-down and bottom-up composables. + +A `rootOwner` refers to the owner address at the top of a tree of composables and ERC721 tokens. An `immediateOwner` refers to an address that directly owns a composable or ERC721 token with no composables between it and the token it owns. + +Authentication within any composable is done by finding the rootOwner and comparing it to `msg.sender`, the return result of `getApproved(tokenId)` and the return result of `isApprovedForAll(rootOwner, msg.sender)`. If no match is found then the immediateOwner of the composable is found and compared to `msg.sender` and the return result of `isApprovedForAll(immediateOwner, msg.sender)`. If a match is found then authentication passes, otherwise authentication fails and the contract throws. + +Here is an example of authentication code: +```solidity +require(rootOwner == msg.sender || isApprovedForAll(rootOwner,msg.sender) || + getApproved(tokenId) == msg.sender || immediateOwner == msg.sender || + isApprovedForAll(immediateOwner,msg.sender); +``` + +The `approve(address _approved, uint256 _tokenId)` and `getApproved(uint256 _tokenId)` ERC721 functions are implemented specifically for the rootOwner, not the immediateOwner. This enables a tree of composables to be transferred to a new rootOwner without worrying about which addresses have been approved in child composables, because any prior approves can only be used by the prior rootOwner. + +Here are example implementations: +```solidity +function approve(address _approved, uint256 _tokenId) external { + address immediateOwner = tokenIdToTokenOwner[_tokenId]; + require(immediateOwner != address(0)); + address rootOwner = address(rootOwnerOf(_tokenId)); + require(rootOwner == msg.sender || isApprovedForAll(rootOwner,msg.sender) || + immediateOwner == msg.sender || isApprovedForAll(immediateOwner,msg.sender)); + + rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] = _approved; + emit Approval(rootOwner, _approved, _tokenId); +} + +function getApproved(uint256 _tokenId) public view returns (address) { + address rootOwner = address(rootOwnerOf(_tokenId)); + return rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; +} +``` +### Traversal + +The rootOwner of a composable is gotten by calling `rootOwnerOf(uint256 _tokenId)` or `rootOwnerOfChild(address _childContract, uint256 _childTokenId)`. These functions are used by top-down and bottom-up composables to traverse up the tree of composables and ERC721 tokens to find the rootOwner. + +Top-down and bottom-up composables are interoperable with each other. It is possible for a top-down composable to own a bottom-up composable or for a top-down composable to own an ERC721 token that owns a bottom-up token. In any configuration calling `rootOwnerOf(uint256 _tokenID)` on the bottom composable will return the owner address at the top of the ownership tree. + +Tokens/contracts that implement the above authentication and traversal functionality are "composable aware". + +Composables and "composable aware" contracts/tokens must call the `onERC998Removed` function in all their transferFrom/safeTransferFrom functions to notify owning/_from contracts that ERC721 tokens are removed. + +### Composable Transfer Function Parameter Format + +Composable functions that make transfers follow the same parameter format: **from:to:what**. + +For example the ` getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId)` composable function transfers an ERC721 token from an address to a top-down composable. The `_from` parameter is the **from**, the `_tokenId` parameter is the **to** and the `address _childContract, uint256 _childTokenId` parameters are the **what**. + +The `transferChild/safeTransferChild` transfer functions do not have a **from** because it is already known that the **from** is `this`, the current contract. + +### ERC721 Top-Down Composable + +ERC721 top-down composables act as containers for ERC721 tokens. + +ERC721 top-down composables are ERC721 tokens that can receive, hold and transfer ERC721 tokens. + +There are two ways to transfer a ERC721 token to a top-down composable: +1. Use the `function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)` function. The `_to` argument is the top-down composable contract address. The `bytes data` argument holds the integer value of the top-down composable tokenId that the ERC721 token is transferred to. +2. Call `approve` in the ERC721 token contract for the top-down composable contract. Then call `getChild` in the composable contract. + +The first ways is for ERC721 contracts that have a `safeTransferFrom` function. The second way is for contracts that do not have this function such as cryptokitties. + +Every ERC721 top-down composable compliant contract must implement the ERC998ERC721TopDown interface. + +The ERC998ERC721TopDownEnumerable and ERC998ERC20TopDownEnumerable interfaces are optional. + +```solidity +pragma solidity ^0.4.24; + +/// @title ERC998ERC721 Top-Down Composable Non-Fungible Token +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md +/// Note: the ERC-165 identifier for this interface is 0x1bc995e4 +interface ERC998ERC721TopDown { + + /// @dev This emits when a token receives a child token. + /// @param _from The prior owner of the token. + /// @param _tokenId The token that receives the child token. + event ReceivedChild( + address indexed _from, + uint256 indexed _tokenId, + address indexed _childContract, + uint256 _childTokenId + ); + + /// @dev This emits when a child token is transferred from a token to an address. + /// @param _tokenId The parent token that the child token is being transferred from. + /// @param _to The new owner address of the child token. + event TransferChild( + uint256 indexed tokenId, + address indexed _to, + address indexed _childContract, + uint256 _childTokenId + ); + + /// @notice Get the root owner of tokenId. + /// @param _tokenId The token to query for a root owner address + /// @return rootOwner The root owner at the top of tree of tokens. + function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner); + + /// @notice Get the root owner of a child token. + /// @param _childContract The contract address of the child token. + /// @param _childTokenId The tokenId of the child. + /// @return rootOwner The root owner at the top of tree of tokens. + function rootOwnerOfChild(address _childContract, uint256 _childTokenId) public view returns (bytes32 rootOwner); + + /// @notice Get the parent tokenId of a child token. + /// @param _childContract The contract address of the child token. + /// @param _childTokenId The tokenId of the child. + /// @return parentTokenOwner The parent address of the parent token + /// @return parentTokenId The parent tokenId of _tokenId + function ownerOfChild(address _childContract, uint256 _childTokenId) + external + view + returns (address parentTokenOwner, uint256 parentTokenId); + + /// @notice A token receives a child token + /// @param _operator The address that caused the transfer. + /// @param _from The owner of the child token. + /// @param _childTokenId The token that is being transferred to the parent. + /// @param _data Up to the first 32 bytes contains an integer which is the receiving parent tokenId. + function onERC721Received(address _operator, address _from, uint256 _childTokenId, bytes _data) + external + returns(bytes4); + + /// @notice Let's an owning contract know that a specified token is transferred out + /// @param The address that caused the transfer. + /// @param _toContract The contract that receives the child token + /// @param _childTokenId The child tokenId that is being removed. + /// @param _data Additional data with no specified format + function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId, bytes _data) external; + + /// @notice Transfer child token from top-down composable to address. + /// @param _to The address that receives the child token + /// @param _childContract The ERC721 contract of the child token. + /// @param _childTokenId The tokenId of the token that is being transferred. + function transferChild(address _to, address _childContract, uint256 _childTokenId) external; + + /// @notice Transfer child token from top-down composable to address. + /// @param _to The address that receives the child token + /// @param _childContract The ERC721 contract of the child token. + /// @param _childTokenId The tokenId of the token that is being transferred. + function safeTransferChild(address _to, address _childContract, uint256 _childTokenId) external; + + /// @notice Transfer child token from top-down composable to address. + /// @param _to The address that receives the child token + /// @param _childContract The ERC721 contract of the child token. + /// @param _childTokenId The tokenId of the token that is being transferred. + /// @param _data Additional data with no specified format + function safeTransferChild(address _to, address _childContract, uint256 _childTokenId, bytes _data) external; + + /// @notice Get a child token from an ERC721 contract. + /// @param _from The address that owns the child token. + /// @param _tokenId The token that becomes the parent owner + /// @param _childContract The ERC721 contract of the child token + /// @param _childTokenId The tokenId of the child token + function getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId) external; +} +``` + +#### rootOwnerOf +```solidity +/// @notice Get the root owner of tokenId. +/// @param _tokenId The token to query for a root owner address +/// @return rootOwner The root owner at the top of tree of tokens. +function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner); +``` + +This function traverses token owners until the the root owner address of `_tokenId` is found. + +The first 4 bytes of rootOwner contain the ERC998 magic value `0x62ff4869`. The last 20 bytes contain the root owner address. + +The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `rootOwnerOf` function. The magic value is used in such calls to ensure a valid return value is received. + +If it is unknown whether a contract has the `rootOwnerOf` function then the magic value must be compared to `0x62ff4869`. + +`0x62ff4869` is equal to: `this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector;` + +Here is an example of a value returned by `rootOwnerOf`. +`0x62ff48690000000000000000e5240103e1ff986a2c8ae6b6728ffe0d9a395c59` + +#### rootOwnerOfChild +```solidity +/// @notice Get the root owner of a child token. +/// @param _childContract The contract address of the child token. +/// @param _childTokenId The tokenId of the child. +/// @return rootOwner The root owner at the top of tree of tokens. +function rootOwnerOfChild(address _childContract, uint256 _childTokenId) public view returns (bytes32 rootOwner); +``` + +This function traverses token owners until the the root owner address of the supplied child token is found. + +The first 4 bytes of rootOwner contain the ERC998 magic value `0x62ff4869`. The last 20 bytes contain the root owner address. + +The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `rootOwnerOf` function. The magic value is used in such calls to ensure a valid return value is received. + +If it is unknown whether a contract has the `rootOwnerOf` function then the magic value must be compared to `0x62ff4869`. + +#### ownerOfChild + +```solidity +/// @notice Get the parent tokenId of a child token. +/// @param _childContract The contract address of the child token. +/// @param _childTokenId The tokenId of the child. +/// @return parentTokenOwner The parent address of the parent token +/// @return parentTokenId The parent tokenId of _tokenId +function ownerOfChild(address _childContract, uint256 _childTokenId) + external + view + returns (address parentTokenOwner, uint256 parentTokenId); +``` + +This function is used to get the parent tokenId of a child token and get the owner address of the parent token. + + +#### onERC721Received +```solidity +/// @notice A token receives a child token +/// @param _operator The address that caused the transfer. +/// @param _from The prior owner of the child token. +/// @param _childTokenId The token that is being transferred to the parent. +/// @param _data Up to the first 32 bytes contains an integer which is the receiving parent tokenId. +function onERC721Received(address _operator, address _from, uint256 _childTokenId, bytes _data) + external + returns(bytes4); +``` + +This is a function defined in the ERC721 standard. This function is called in an ERC721 contract when `safeTransferFrom` is called. The `bytes _data` argument contains an integer value from 1 to 32 bytes long that is the parent tokenId that an ERC721 token is transferred to. + +The `onERC721Received` function is how a top-down composable contract is notified that an ERC721 token has been transferred to it and what tokenId in the top-down composable is the parent tokenId. + +The return value for `onERC721Received` is the magic value `0x150b7a02` which is equal to `bytes4(keccak256(abi.encodePacked("onERC721Received(address,address,uint256,bytes)")))`. + +#### onERC998Removed +```solidity +/// @notice Let's an owning contract know that a specified token is transferred out +/// @param The address that caused the transfer. +/// @param _toContract The contract that receives the child token +/// @param _childTokenId The child tokenId that is being removed. +/// @param _data Additional data with no specified format +function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId, bytes _data) external; +``` + +Any composable or "composable aware" contract must call the `onERC998Removed` function within all `transferFrom/safeTransferFrom` functions on the contract that is losing ownership of an ERC721 token. It is used to notify top-down composables that they no longer have a child token and to remove it from any internal bookkeeping/tracking. + +Contracts that call `onERC998Removed` must not throw if the call fails because it is possible that a contract does not have the `onERC998Removed` function. + +#### transferChild +```solidity +/// @notice Transfer child token from top-down composable to address. +/// @param _to The address that receives the child token +/// @param _childContract The ERC721 contract of the child token. +/// @param _childTokenId The tokenId of the token that is being transferred. +function transferChild(address _to, address _childContract, uint256 _childTokenId) external; +``` + +This function authenticates `msg.sender` and transfers a child token from a top-down composable to a different address. + +This function makes this call within it: `ERC721(_childContract).transferFrom(this, _to, _childTokenId);` + +#### safeTransferChild +```solidity +/// @notice Transfer child token from top-down composable to address. +/// @param _to The address that receives the child token +/// @param _childContract The ERC721 contract of the child token. +/// @param _childTokenId The tokenId of the token that is being transferred. +function safeTransferChild(address _to, address _childContract, uint256 _childTokenId) external; +``` + +This function authenticates `msg.sender` and transfers a child token from a top-down composable to a different address. + +This function makes this call within it: `ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId);` + +#### safeTransferChild +```solidity +/// @notice Transfer child token from top-down composable to address or other top-down composable. +/// @param _to The address that receives the child token +/// @param _childContract The ERC721 contract of the child token. +/// @param _childTokenId The tokenId of the token that is being transferred. +/// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to +function safeTransferChild(address _to, address _childContract, uint256 _childTokenId, bytes _data) external; +``` + +This function authenticates `msg.sender` and transfers a child token from a top-down composable to a different address or to a different top-down composable. + +A child token is transferred to a different top-down composable if the `_to` address is a top-down composable contract and `bytes _data` is supplied an integer representing the parent tokenId. + +This function makes this call within it: `ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId, _data);` + +#### getChild +```solidity +/// @notice Get a child token from an ERC721 contract. +/// @param _from The address that owns the child token. +/// @param _tokenId The token that becomes the parent owner +/// @param _childContract The ERC721 contract of the child token +/// @param _childTokenId The tokenId of the child token +function getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId) external; +``` + +This function is used to transfer an ERC721 token when its contract does not have a `safeTransferChild(address _to, address _childContract, uint256 _childTokenId, bytes _data)` function. + +A transfer with this function is done in two steps: +1. The owner of the ERC721 token calls `approve` or `setApprovalForAll` in the ERC721 contract for the top-down composable contract. +2. The owner of the ERC721 token calls `getChild` in the top-down composable contract for the ERC721 token. + +The `getChild` function must authenticate that `msg.sender` is the owner of the ERC721 token in the ERC721 contract or is approved or an operator of the ERC721 token in the ERC721 contract. + +#### ERC721 Top-Down Composable Enumeration +Optional interface for top-down composable enumeration: + +```solidity +/// @dev The ERC-165 identifier for this interface is 0xa344afe4 +interface ERC998ERC721TopDownEnumerable { + + /// @notice Get the total number of child contracts with tokens that are owned by tokenId. + /// @param _tokenId The parent token of child tokens in child contracts + /// @return uint256 The total number of child contracts with tokens owned by tokenId. + function totalChildContracts(uint256 _tokenId) external view returns(uint256); + + /// @notice Get child contract by tokenId and index + /// @param _tokenId The parent token of child tokens in child contract + /// @param _index The index position of the child contract + /// @return childContract The contract found at the tokenId and index. + function childContractByIndex(uint256 _tokenId, uint256 _index) external view returns (address childContract); + + /// @notice Get the total number of child tokens owned by tokenId that exist in a child contract. + /// @param _tokenId The parent token of child tokens + /// @param _childContract The child contract containing the child tokens + /// @return uint256 The total number of child tokens found in child contract that are owned by tokenId. + function totalChildTokens(uint256 _tokenId, address _childContract) external view returns(uint256); + + /// @notice Get child token owned by tokenId, in child contract, at index position + /// @param _tokenId The parent token of the child token + /// @param _childContract The child contract of the child token + /// @param _index The index position of the child token. + /// @return childTokenId The child tokenId for the parent token, child token and index + function childTokenByIndex(uint256 _tokenId, address _childContract, uint256 _index) + external + view + returns (uint256 childTokenId); +} +``` + +### ERC20 Top-Down Composable + +ERC20 top-down composables act as containers for ERC20 tokens. + +ERC20 top-down composables are ERC721 tokens that can receive, hold and transfer ERC20 tokens. + +There are two ways to transfer ERC20 tokens to an ERC20 Top-Down Composable: + +1. Use the `transfer(address _to, uint256 _value, bytes _data);` function from the ERC223 contract. The `_to` argument is the ERC20 top-down composable contract address. The `_value` argument is how many ERC20 tokens to transfer. The `bytes` argument holds the integer value of the top-down composable tokenId that receives the ERC20 tokens. +2. Call `approve` in the ERC20 contract for the ERC20 top-down composable contract. Then call `getERC20(address _from, uint256 _tokenId, address _erc20Contract, uint256 _value)` from the ERC20 top-down composable contract. + +The first way is for ERC20 contracts that support the ERC223 standard. The second way is for contracts that do not. + +ERC20 top-down composables implement the following interface: + +```solidity +/// @title ERC998ERC20 Top-Down Composable Non-Fungible Token +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md +/// Note: the ERC-165 identifier for this interface is 0x7294ffed +interface ERC998ERC20TopDown { + + /// @dev This emits when a token receives ERC20 tokens. + /// @param _from The prior owner of the token. + /// @param _tokenId The token that receives the ERC20 tokens. + /// @param _erc20Contract The ERC20 contract. + /// @param _value The number of ERC20 tokens received. + event ReceivedERC20( + address indexed _from, + uint256 indexed _tokenId, + address indexed _erc20Contract, + uint256 _value + ); + + /// @dev This emits when a token transfers ERC20 tokens. + /// @param _tokenId The token that owned the ERC20 tokens. + /// @param _to The address that receives the ERC20 tokens. + /// @param _erc20Contract The ERC20 contract. + /// @param _value The number of ERC20 tokens transferred. + event TransferERC20( + uint256 indexed _tokenId, + address indexed _to, + address indexed _erc20Contract, + uint256 _value + ); + + /// @notice A token receives ERC20 tokens + /// @param _from The prior owner of the ERC20 tokens + /// @param _value The number of ERC20 tokens received + /// @param _data Up to the first 32 bytes contains an integer which is the receiving tokenId. + function tokenFallback(address _from, uint256 _value, bytes _data) external; + + /// @notice Look up the balance of ERC20 tokens for a specific token and ERC20 contract + /// @param _tokenId The token that owns the ERC20 tokens + /// @param _erc20Contract The ERC20 contract + /// @return The number of ERC20 tokens owned by a token from an ERC20 contract + function balanceOfERC20(uint256 _tokenId, address _erc20Contract) external view returns(uint256); + + /// @notice Transfer ERC20 tokens to address + /// @param _tokenId The token to transfer from + /// @param _value The address to send the ERC20 tokens to + /// @param _erc20Contract The ERC20 contract + /// @param _value The number of ERC20 tokens to transfer + function transferERC20(uint256 _tokenId, address _to, address _erc20Contract, uint256 _value) external; + + /// @notice Transfer ERC20 tokens to address or ERC20 top-down composable + /// @param _tokenId The token to transfer from + /// @param _value The address to send the ERC20 tokens to + /// @param _erc223Contract The ERC223 token contract + /// @param _value The number of ERC20 tokens to transfer + /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to + function transferERC223(uint256 _tokenId, address _to, address _erc223Contract, uint256 _value, bytes _data) + external; + + /// @notice Get ERC20 tokens from ERC20 contract. + /// @param _from The current owner address of the ERC20 tokens that are being transferred. + /// @param _tokenId The token to transfer the ERC20 tokens to. + /// @param _erc20Contract The ERC20 token contract + /// @param _value The number of ERC20 tokens to transfer + function getERC20(address _from, uint256 _tokenId, address _erc20Contract, uint256 _value) external; +} +``` +#### tokenFallback +```solidity +/// @notice A token receives ERC20 tokens +/// @param _from The prior owner of the ERC20 tokens +/// @param _value The number of ERC20 tokens received +/// @param _data Up to the first 32 bytes contains an integer which is the receiving tokenId. +function tokenFallback(address _from, uint256 _value, bytes _data) external; +``` + +This function comes from the [ERC223 standard](https://github.com/ethereum/EIPs/issues/223) which is an extension of the ERC20 standard. This function is called on the receiving contract from the sending contract when ERC20 tokens are transferred. This function is how the ERC20 top-down composable contract gets notified that one of its tokens received ERC20 tokens. Which token received ERC20 tokens is specified in the `_data` parameter. + +#### balanceOfERC20 +```solidity +/// @notice Look up the balance of ERC20 tokens for a specific token and ERC20 contract +/// @param _tokenId The token that owns the ERC20 tokens +/// @param _erc20Contract The ERC20 contract +/// @return The number of ERC20 tokens owned by a token from an ERC20 contract +function balanceOfERC20(uint256 _tokenId, address _erc20Contract) external view returns(uint256); +``` + +Gets the balance of ERC20 tokens owned by a token from a specific ERC20 contract. + +#### transferERC20 +```solidity +/// @notice Transfer ERC20 tokens to address +/// @param _tokenId The token to transfer from +/// @param _value The address to send the ERC20 tokens to +/// @param _erc20Contract The ERC20 contract +/// @param _value The number of ERC20 tokens to transfer +function transferERC20(uint256 _tokenId, address _to, address _erc20Contract, uint256 _value) external; +``` + +This is used to transfer ERC20 tokens from a token to an address. This function calls `ERC20(_erc20Contract).transfer(_to, _value)`; + +This function must authenticate `msg.sender`. + +#### transferERC223 +```solidity + /// @notice Transfer ERC20 tokens to address or ERC20 top-down composable + /// @param _tokenId The token to transfer from + /// @param _value The address to send the ERC20 tokens to + /// @param _erc223Contract The ERC223 token contract + /// @param _value The number of ERC20 tokens to transfer + /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to + function transferERC223(uint256 _tokenId, address _to, address _erc223Contract, uint256 _value, bytes _data) + external; +``` + +This function is from the [ERC223 standard](https://github.com/ethereum/EIPs/issues/223). It is used to transfer ERC20 tokens from a token to an address or to another token by putting an integer token value in the `_data` argument. + +This function must authenticate `msg.sender`. + +#### getERC20 +```solidity +/// @notice Get ERC20 tokens from ERC20 contract. +/// @param _from The current owner address of the ERC20 tokens that are being transferred. +/// @param _tokenId The token to transfer the ERC20 tokens to. +/// @param _erc20Contract The ERC20 token contract +/// @param _value The number of ERC20 tokens to transfer +function getERC20(address _from, uint256 _tokenId, address _erc20Contract, uint256 _value) external; +``` + +This function is used to transfer ERC20 tokens to an ERC20 top-down composable when an ERC20 contract does not have a `transferERC223(uint256 _tokenId, address _to, address _erc223Contract, uint256 _value, bytes _data)` function. + +Before this function can be used the ERC20 top-down composable contract address must be approved in the ERC20 contract to transfer the ERC20 tokens. + +This function must authenticate that `msg.sender` equals `_from` or has been approved in the ERC20 contract. + +#### ERC20 Top-Down Composable Enumeration +Optional interface for top-down composable enumeration: + +```solidity +/// @dev The ERC-165 identifier for this interface is 0xc5fd96cd +interface ERC998ERC20TopDownEnumerable { + + /// @notice Get the number of ERC20 contracts that token owns ERC20 tokens from + /// @param _tokenId The token that owns ERC20 tokens. + /// @return uint256 The number of ERC20 contracts + function totalERC20Contracts(uint256 _tokenId) external view returns(uint256); + + /// @notice Get an ERC20 contract that token owns ERC20 tokens from by index + /// @param _tokenId The token that owns ERC20 tokens. + /// @param _index The index position of the ERC20 contract. + /// @return address The ERC20 contract + function erc20ContractByIndex(uint256 _tokenId, uint256 _index) external view returns(address); +} +``` + +### ERC721 Bottom-Up Composable + +ERC721 bottom-up composables are ERC721 tokens that attach themselves to other ERC721 tokens. + +ERC721 bottom-up composable contracts store the owning address of a token and the parent tokenId if any. + +```solidity +/// @title ERC998ERC721 Bottom-Up Composable Non-Fungible Token +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md +/// Note: the ERC-165 identifier for this interface is 0xa1b23002 +interface ERC998ERC721BottomUp { + + /// @dev This emits when a token is transferred to an ERC721 token + /// @param _toContract The contract the token is transferred to + /// @param _toTokenId The token the token is transferred to + /// @param _tokenId The token that is transferred + event TransferToParent( + address indexed _toContract, + uint256 indexed _toTokenId, + uint256 _tokenId + ); + + /// @dev This emits when a token is transferred from an ERC721 token + /// @param _fromContract The contract the token is transferred from + /// @param _fromTokenId The token the token is transferred from + /// @param _tokenId The token that is transferred + event TransferFromParent( + address indexed _fromContract, + uint256 indexed _fromTokenId, + uint256 _tokenId + ); + + /// @notice Get the root owner of tokenId. + /// @param _tokenId The token to query for a root owner address + /// @return rootOwner The root owner at the top of tree of tokens. + function rootOwnerOf(uint256 _tokenId) external view returns (bytes rootOwner); + + /// @notice Get the owner addess and parent token (if there is one) of a token + /// @param _tokenId The tokenId to query. + /// @return tokenOwner The owner address of the token + /// @return parentTokenId The parent owner of the token + /// @return isParent True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId + function tokenOwnerOf(uint256 _tokenId) + external + view + returns (address tokenOwner, uint256 parentTokenId, bool isParent); + + /// @notice Transfer token from owner address to a token + /// @param _from The owner address + /// @param _toContract The ERC721 contract of the receiving token + /// @param _toToken The receiving token + /// @param _data Additional data with no specified format + function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) + external; + + /// @notice Transfer token from a token to an address + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _to The address the token is transferred to. + /// @param _tokenId The token that is transferred + /// @param _data Additional data with no specified format + function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _tokenId, bytes _data) + external; + + /// @notice Transfer a token from a token to another token + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _toContract The ERC721 contract of the receiving token + /// @param _toToken The receiving token + /// @param _tokenId The token that is transferred + /// @param _data Additional data with no specified format + function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) + external; +} +``` + +#### rootOwnerOf +```solidity +/// @notice Get the root owner of tokenId. +/// @param _tokenId The token to query for a root owner address +/// @return rootOwner The root owner at the top of tree of tokens. +function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner); +``` + +This function traverses token owners until the the root owner address of `_tokenId` is found. + +The first 4 bytes of rootOwner contain the ERC998 magic value `0x62ff4869`. The last 20 bytes contain the root owner address. + +The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `rootOwnerOf` function. The magic value is used in such calls to ensure a valid return value is received. + +If it is unknown whether a contract has the `rootOwnerOf` function then the magic value must be compared to `0x62ff4869`. + +`0x62ff4869` is equal to: `this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector;` + +Here is an example of a value returned by `rootOwnerOf`. +`0x62ff48690000000000000000e5240103e1ff986a2c8ae6b6728ffe0d9a395c59` + +#### tokenOwnerOf +```solidity +/// @notice Get the owner addess and parent token (if there is one) of a token +/// @param _tokenId The tokenId to query. +/// @return tokenOwner The owner address of the token +/// @return parentTokenId The parent owner of the token +/// @return isParent True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId +function tokenOwnerOf(uint256 _tokenId) + external + view + returns (address tokenOwner, uint256 parentTokenId, bool isParent); +``` + +This function is used to get the owning address and parent tokenId of a token if there is one stored in the contract. + +If `isParent` is true then `tokenOwner` is the owning ERC721 contract address and `parentTokenId` is a valid parent tokenId. If `isParent` is false then `tokenOwner` is a user address and `parentTokenId` does not contain a valid parent tokenId and must be ignored. + +#### transferToParent +```solidity +/// @notice Transfer token from owner address to a token +/// @param _from The owner address +/// @param _toContract The ERC721 contract of the receiving token +/// @param _toToken The receiving token +/// @param _data Additional data with no specified format +function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) + external; +``` + +This function is used to transfer a token from an address to a token. `msg.sender` must be authenticated. + +This function must check that `_toToken` exists in `_toContract` and throw if not. + + +#### transferFromParent +```solidity +/// @notice Transfer token from a token to an address +/// @param _fromContract The address of the owning contract +/// @param _fromTokenId The owning token +/// @param _to The address the token is transferred to. +/// @param _tokenId The token that is transferred +/// @param _data Additional data with no specified format +function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _tokenId, bytes _data) + external; +``` +This function is used to transfer a token from a token to an address. `msg.sender` must be authenticated. + +This function must check that `_fromContract` and `_fromTokenId` own `_tokenId` and throw not. + +#### transferAsChild +```solidity +/// @notice Transfer a token from a token to another token +/// @param _fromContract The address of the owning contract +/// @param _fromTokenId The owning token +/// @param _toContract The ERC721 contract of the receiving token +/// @param _toToken The receiving token +/// @param _tokenId The token that is transferred +/// @param _data Additional data with no specified format +function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) + external; +``` + +This function is used to transfer a token from a token to another token. `msg.sender` must be authenticated. + +This function must check that `_toToken` exists in `_toContract` and throw if not. + +This function must check that `_fromContract` and `_fromTokenId` own `_tokenId` and throw if not. + +#### ERC721 Bottom-Up Composable Enumeration +Optional interface for bottom-up composable enumeration: + +```solidity +/// @dev The ERC-165 identifier for this interface is 0x8318b539 +interface ERC998ERC721BottomUpEnumerable { + + /// @notice Get the number of ERC721 tokens owned by parent token. + /// @param _parentContract The contract the parent ERC721 token is from. + /// @param _parentTokenId The parent tokenId that owns tokens + // @return uint256 The number of ERC721 tokens owned by parent token. + function totalChildTokens(address _parentContract, uint256 _parentTokenId) external view returns (uint256); + + /// @notice Get a child token by index + /// @param _parentContract The contract the parent ERC721 token is from. + /// @param _parentTokenId The parent tokenId that owns the token + /// @param _index The index position of the child token + /// @return uint256 The child tokenId owned by the parent token + function childTokenByIndex(address _parentContract, uint256 _parentTokenId, uint256 _index) + external + view + returns (uint256); +} +``` + +## Rationale +Two different kinds of composable (top-down and bottom-up) exist to handle different use cases. A regular ERC721 token cannot own a top-down composable, but it can own a bottom-up composable. A bottom-up composable cannot own a regular ERC721 but a top-down composable can own a regular ERC721 token. Having multiple kinds of composables enable different token ownership possibilities. + +#### Which Kind of Composable To Use? + +If you want to transfer regular ERC721 tokens to non-fungible tokens, then use top-down composables. + +If you want to transfer non-fungible tokens to regular ERC721 tokens then use bottom-up composables. + +### Additional Reading Material +[Top-Down and Bottom-Up Composables, What's the Difference and Which One Should You Use?](https://hackernoon.com/top-down-and-bottom-up-composables-whats-the-difference-and-which-one-should-you-use-db939f6acf1d) + +## Backwards Compatibility +Composables are designed to work with ERC721, ERC223 and ERC20 tokens. + +Some older ERC721 contracts do not have a `safeTransferFrom` function. The `getChild` function can still be used to transfer a token to an ERC721 top-down composable. + +If an ERC20 contract does not have the ERC223 function `transfer(address _to, uint _value, bytes _data)` then the `getERC20` function can still be used to transfer ERC20 tokens to an ERC20 top-down composable. + + +## Implementations + +An implementation can be found here: https://github.com/mattlockyer/composables-998 + + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + + + From 4315fe97bb59a3ede83674470ee911b68d4d551b Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Tue, 24 Jul 2018 17:15:12 -0700 Subject: [PATCH 063/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index dc99ebc3..d8b2df39 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -118,7 +118,7 @@ pragma solidity ^0.4.24; /// @title ERC998ERC721 Top-Down Composable Non-Fungible Token /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md -/// Note: the ERC-165 identifier for this interface is 0x1bc995e4 +/// Note: the ERC-165 identifier for this interface is 0xe85c4c41 interface ERC998ERC721TopDown { /// @dev This emits when a token receives a child token. @@ -172,11 +172,10 @@ interface ERC998ERC721TopDown { returns(bytes4); /// @notice Let's an owning contract know that a specified token is transferred out - /// @param The address that caused the transfer. + /// @param _operator The address that caused the transfer. /// @param _toContract The contract that receives the child token - /// @param _childTokenId The child tokenId that is being removed. - /// @param _data Additional data with no specified format - function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId, bytes _data) external; + /// @param _childTokenId The child tokenId that is being removed. + function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId) external; /// @notice Transfer child token from top-down composable to address. /// @param _to The address that receives the child token @@ -285,8 +284,7 @@ The return value for `onERC721Received` is the magic value `0x150b7a02` which is /// @param The address that caused the transfer. /// @param _toContract The contract that receives the child token /// @param _childTokenId The child tokenId that is being removed. -/// @param _data Additional data with no specified format -function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId, bytes _data) external; +function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId) external; ``` Any composable or "composable aware" contract must call the `onERC998Removed` function within all `transferFrom/safeTransferFrom` functions on the contract that is losing ownership of an ERC721 token. It is used to notify top-down composables that they no longer have a child token and to remove it from any internal bookkeeping/tracking. From ae8c494d42280cbdcb778fd1e7b39bf94c292bae Mon Sep 17 00:00:00 2001 From: Zainan Zhou Date: Wed, 25 Jul 2018 17:07:52 +0400 Subject: [PATCH 065/177] Automatically merged updates to draft EIP(s) 1202 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1202.md | 117 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-1202.md b/EIPS/eip-1202.md index ab6b7a43..a114f2e2 100644 --- a/EIPS/eip-1202.md +++ b/EIPS/eip-1202.md @@ -1,7 +1,7 @@ --- eip: 1202 title: Voting Standard -author: Zainan Victor Zhou (@xinbenlv), Evan (@evbots) +author: Zainan Victor Zhou (@xinbenlv), Evan (@evbots), Yin Xu (@yingogobot) type: Standards Track category: ERC status: Draft @@ -9,13 +9,27 @@ created: 2018-07-08 discussions-to: https://github.com/ethereum/EIPs/issues/1202 --- -Note: we are still open to have co-author to collaborate. +## Note to Readers + +1. We are still open to have co-author to collaborate, in particular, we are looking for co-authors of the +following category: + - standard designers who are experienced in application-layer standard design, or + - developers who have experience with blockchain-based voting system in practice + - researchers with research interest in crypto/zero-knowledge-proof voting + + +2. We have two discussion destinations: + - [Github Ethereum EIP Issue #1202](https://github.com/ethereum/EIPs/issues/1202) for long and more mature thoughts + - [Telegram Channel t.me/erc1202](https://t.me/erc1202) for real-time and related random chat. + +3. We are actively working on updating this draft as many feedbacks have come in since it merged into official EIP repo. +If you are viewing a snapshot of this draft, please be adviced the latest dev version of ERC 1202 can be found [here](https://github.com/xinbenlv/eip-1202-draft/blob/master/EIP-1202.md) ## Simple Summary Propose a standard interface for voting. ## Abstract -This proposal creates a standard API for implementing voting within smart contract. This standard provides basic functionality to voting as well as to view the vote result and set voting status. +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. ## 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 @@ -121,8 +135,20 @@ We made the following design decisions and here are the rationales. * 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. + +### Security and Privacy of Voting +// TODO -## Examples +## Backward Compatibility +There is no backward compatibility issue we are aware of. + + +## Interactions with other ERCs +// TODO add interaction discussion for the following ERCs +ERC20, ERC721, ERC735, ERC780, ERC165 + + +## Simple Code Examples ### Example 1: Simplest Version: Single Issue Yes/No Question Per Smart Contract Address Per Non-Weighted Vote - [Source Code](https://github.com/xinbenlv/eip-1202-draft/blob/master/contracts/simple-version/SimplestVote1202.sol) @@ -136,6 +162,58 @@ We made the following design decisions and here are the rationales. - [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) +## Comprehensive Application Examples + +### Example 1: Joint Wallet Account Spending Approval +// TODO + +### Example 2: Secret Vote +// TODO + +### Example 3: Token Re-issue +// TODO + +### Example 4: Multi-input Oracle +// TODO + + +## Case Study + +### Existing Voting Systems in Blockchain World +// TODO + +#### Carbon Vote +// TODO + +#### PLACE Voting +https://medium.com/@jameson.quinn/how-place-voting-works-617a5e8ac422 + +#### PLCR Voting +[PLCR Voting: ](https://github.com/ConsenSys/PLCRVoting) +https://medium.com/metax-publication/a-walkthrough-of-plcr-voting-in-solidity-92420bd5b87c + +#### + +### Exiting Voting Systems in Real World +// TODO + +#### Simple Majority Vote Requiring Quorum (e.g. Company Board) +// TODO, and a small variant: ZaiGeZaiGu function committee approval (1/2 as quorum, majority vote) + + +#### Two-tiered Shareholder Vote (e.g. GOOG, FB) +// TODO + +#### Jury Decision of US Federal Criminal Court (All Ayle for Guity, 5/5 for Tie) +// TODO + +#### US Presidential Election: Different Vote Time, Multi-Reginal, Two-level (General and Editorial(delegate)) +// TODO + +#### Super-Girl China 2005: Idol Ranking Vote, Multiple Votes Allowed +// TODO + + ## Summary of Discussions ### Early Feedback Questions (2018-07-08) @@ -166,6 +244,8 @@ Here are a few early questions I'd like to ask people here. - [EIP-20: ERC-20 Token Standard (a.k.a. ERC-20)](https://eips.ethereum.org/EIPS/eip-20) - [EIP-165: Standard Interface Detection](https://eips.ethereum.org/EIPS/eip-165) - [EIP-721: Non-Fungible Token Standard(a.k.a. ERC-721)](https://eips.ethereum.org/EIPS/eip-721) + - [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](https://eips.ethereum.org/EIPS/eip-777) - [EIP-897: ERC DelegateProxy](https://eips.ethereum.org/EIPS/eip-897) - [EIP-1155: Crypto Item Standard](https://eips.ethereum.org/EIPS/eip-1155) @@ -173,26 +253,43 @@ Here are a few early questions I'd like to ask people here. - [EIP-1167: Minimal Proxy Contract](https://eips.ethereum.org/EIPS/eip-1167) - [EIP-1203: Multi-class Token Standard(ERC-20 Extension)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1203.md) -### Related Projects +### 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) + +### Worthnoting Academic Papers -### Request for Comment + +## Request for Comment We kindly request the community for comments, in particular, the following ERC and projects related authors: - ERC-20: @frozeman, @vbuterin - ERC-721: @fulldecent, Dieter Shirley, Jacob Evans, Nastassia Sachs - Carbon Vote: @lgn21st, @Aaaaaashu - + - Alex Van de Sande (Mist) and Nick Johnson (ENS) * - suggested by Fabian (@frozeman)* + - Will Warren, 0xProject a project who cares a lot about governance. * - nominated by Evan()@evanbots)* + Your comments and suggestions will be greatly appreciated. -## EIP WIP Work Logs +## Acknowledgement +The authors of EIP 1202 greatly appreciate the valuable input from distinguished community members including: @frozeman, @fulldecent, @bingen, @aodhgan. + +## EIP Work Logs - 2018-07-08: (@xinbenlv) Created early feedback request. Asked around discussion channels suggested in [EIP-1](https://eips.ethereum.org/EIPS/eip-1), such as [Ethereum-Magicians](https://ethereum-magicians.org/t/eip-x-voting-standard-early-feedback-wanted/670/2), [Gitter](https://gitter.im/ethereum/EIPs), [Reddit](https://www.reddit.com/r/ethereum/comments/8x6k11/early_feedback_request_for_eipx_voting_standard/) -- 2018-07-09: (@xinbenlv)Added examples layout. Request for co-author. +- 2018-07-09: (@xinbenlv)Added examples outline. Request for co-author. - 2018-07-17: (@xinbenlv)Added co-author. @evbots, added two simple examples. - 2018-07-19: (@xinbenlv)Added interface-like specification. Moved content from [issue](https://github.com/ethereum/EIPs/issues/1202) to [xinbenlv's Github repo](https://github.com/xinbenlv/eip-1202-draft/blob/master/EIP-1202.md) . Added TokenVote example. -- 2018-07-20: (@xinbenlv)Added advanced token vote exmaple. +- 2018-07-20: (@xinbenlv)Added advanced token vote example. +- 2018-07-22: (@xinbenlv)Moved official discussion thread from [github issue](https://github.com/ethereum/EIPs/issues/1202) to [ethereum-magicians](https://ethereum-magicians.org/t/erc-1202-voting-standard-official-discussion-thread/670) - moved back as it's ok to use GitHub issue as official discussion thread. +- 2018-07-23: (@xinbenlv) + - added co-author Yin Xu (@yingogobot) + - added outline for a few applications and case studies for further drafting. + - added citation of ERC-735 and ERC-780 +- 2018-07-25: (@xinbenlv) Added input from @fulldecent, @bingen, @aodhgan to mention the privacy casting, PLCR etc, and +added placeholders for related session. ## Copyright From 73098c11536fad3a9dce4b7b5b2f14c3b8ba5e7b Mon Sep 17 00:00:00 2001 From: yarrumretep Date: Thu, 26 Jul 2018 13:51:54 -0400 Subject: [PATCH 066/177] Automatically merged updates to draft EIP(s) 1167 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1167.md | 180 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index 1e24d58e..d226cfef 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -16,7 +16,7 @@ created: 2018-06-22 To simply and cheaply clone contract functionality in an immutable way, we propose to standardize on a minimal bytecode implementation which delegates all calls to a known, fixed address. ## Abstract -By standardizing on a known minimal bytecode redirect implementation, this standard will allow 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. Specifically, tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run - and can depend on representations about that code (verified source, third-party audits, etc). We have an implementation we believe to be minimal and covering both standard calls and +By standardizing on a known minimal bytecode redirect implementation, this standard will allow 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. Specifically, tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run - and can depend on representations about that code (verified source, third-party audits, etc). This implementation forwards all calls and 100% of the gas 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 (for revert with message). ## Motivation @@ -25,7 +25,9 @@ This standard is desireable to allow for use-cases wherein it is desireable to c ## Specification -The exact bytecode of the standard clone contract is this: `6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd` wherein the bytes at idices 10 - 29 (inclusive) are replaced with the 20 byte address of the master functionality contract. The reference implementation of this is found at the [optionality/clone-factory](https://github.com/optionality/clone-factory) github repo. There are variations as well for using vanity contract addresses with leading zeros to further shrink the necessary clone bytecode (thus making it cheaper to deploy). Detection of clone and redirection is implemented in the clone-factory repo with a contract deployed on both Kovan and Mainnet that detects the presence of a clone and returns the destination address if the interrogated contract is a clone (handles shortened addresses as well). +The exact bytecode of the standard clone contract is this: `6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd` wherein the bytes at idices 10 - 29 (inclusive) are replaced with the 20 byte address of the master functionality contract. The reference implementation of this is found at the [optionality/clone-factory](https://github.com/optionality/clone-factory) github repo. + +Detection of clone and redirection is implemented in the clone-factory repo with a contract deployed on both Kovan and Mainnet that detects the presence of a clone and returns the destination address if the interrogated contract is a clone (handles shortened addresses as well). ## Rationale @@ -47,7 +49,179 @@ We have included some simple test cases in the clone-factory project that demons ## Implementation -Please see [optionality/clone-factory](https://github.com/optionality/clone-factory) +The exact bytecode for deploying the clone instances is `600034603b57603080600f833981f36000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd` with the 20 'be' bytes at offset 26 replaced with the address of the implementation contract. This deployment bytecode results in a deployed contract of `6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd`. +The disassembly of the full deployment code (from r2, then edited to account for deployment offset changes) +``` + 0x00000000 6000 push1 0x0 + 0x00000002 34 callvalue + 0x00000003 603b push1 0x3b + ,=< 0x00000005 57 jumpi + | 0x00000006 6030 push1 0x30 + | 0x00000008 80 dup1 + | 0x00000009 600f push1 0xf + | 0x0000000b 83 dup4 + | 0x0000000c 39 codecopy + | 0x0000000d 81 dup2 + | 0x0000000e f3 return + | 0x0000000f 6000 push1 0x0 + | 0x00000011 36 calldatasize + | 0x00000012 81 dup2 + | 0x00000013 80 dup1 + | 0x00000014 37 calldatacopy + | 0x00000015 80 dup1 + | 0x00000016 80 dup1 + | 0x00000017 36 calldatasize + | 0x00000018 81 dup2 + | 0x00000019 73bebebebebe. push20 0xbebebebe + | 0x0000002e 5a gas + | 0x0000002f f4 delegatecall + | 0x00000030 3d returndatasize + | 0x00000031 82 dup3 + | 0x00000032 80 dup1 + | 0x00000033 3e returndatacopy + | 0x00000034 15 iszero + | 0x00000035 602c push1 0x2c // note that this offset is post deployment the following jumpi "arrow" on the left was hand edited + ,==< 0x00000037 57 jumpi + :| 0x00000038 3d returndatasize + :| 0x00000039 90 swap1 + :| 0x0000003a f3 return + ``-> 0x0000003b 5b jumpdest + 0x0000003c 3d returndatasize + 0x0000003d 90 swap1 + 0x0000003e fd revert +``` + +Disassembly of only the deployed contract bytecode is (straight from r2): +``` +| 0x00000000 6000 push1 0x0 +| 0x00000002 36 calldatasize +| 0x00000003 81 dup2 +| 0x00000004 80 dup1 +| 0x00000005 37 calldatacopy +| 0x00000006 80 dup1 +| 0x00000007 80 dup1 +| 0x00000008 36 calldatasize +| 0x00000009 81 dup2 +| 0x0000000a 73bebebebebe. push20 0xbebebebe +| 0x0000001f 5a gas +| 0x00000020 f4 delegatecall +| 0x00000021 3d returndatasize +| 0x00000022 82 dup3 +| 0x00000023 80 dup1 +| 0x00000024 3e returndatacopy +| 0x00000025 15 iszero +| 0x00000026 602c push1 0x2c +| ,=< 0x00000028 57 jumpi +| | 0x00000029 3d returndatasize +| | 0x0000002a 90 swap1 +| | 0x0000002b f3 return +| `-> 0x0000002c 5b jumpdest +| 0x0000002d 3d returndatasize +| 0x0000002e 90 swap1 +\ 0x0000002f fd revert +``` + +The typical deployment pattern would be to deploy a Factory contract that can easily create clones. Here is the reference implementation of the clone-factory pattern: +```solidity +contract CloneFactory { + + event CloneCreated(address indexed target, address clone); + + function createClone(address target) internal returns (address result) { + bytes memory clone = hex"600034603b57603080600f833981f36000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd"; + bytes20 targetBytes = bytes20(target); + for (uint i = 0; i < 20; i++) { + clone[26 + i] = targetBytes[i]; + } + assembly { + let len := mload(clone) + let data := add(clone, 0x20) + result := create(0, data, len) + } + } +} +``` + +To utilize the above implementation, you would extend the contract like this: +```solidity +import "./Thing.sol"; +import "../contracts/CloneFactory.sol"; + + +contract ThingFactory is CloneFactory { + + address public libraryAddress; + + event ThingCreated(address newThingAddress, address libraryAddress); + + constructor (address _libraryAddress) public { + libraryAddress = _libraryAddress; + } + + function createThing(string _name, uint _value) public { + address clone = createClone(libraryAddress); + Thing(clone).init(_name, _value); + emit ThingCreated(clone, libraryAddress); + } +} + +``` + +IMPORANT NOTE: When implementing, it is important to ensure that the master implementation contract cannot be 'initialized' and that it cannot in any way be selfdestructed. + +Clones can be detected using the following contract +```solidity + +contract ContractProbe { + + function probe(address _addr) public view returns (bool isContract, address forwardedTo) { + bytes memory clone = hex"6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd"; + uint size; + bytes memory code; + + assembly { //solhint-disable-line + size := extcodesize(_addr) + } + + isContract = size > 0; + forwardedTo = _addr; + + if (size <= 48 && size >= 44) { + bool matches = true; + uint i; + + assembly { //solhint-disable-line + code := mload(0x40) + mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + mstore(code, size) + extcodecopy(_addr, add(code, 0x20), 0, size) + } + for (i = 0; matches && i < 10; i++) { + matches = code[i] == clone[i]; + } + for (i = 0; matches && i < 17; i++) { + if (i == 8) { + matches = code[code.length - i - 1] == byte(uint(clone[48 - i - 1]) - (48 - size)); + } else { + matches = code[code.length - i - 1] == clone[48 - i - 1]; + } + } + if (code[10] != byte(0x73 - (48 - size))) { + matches = false; + } + uint forwardedToBuffer; + if (matches) { + assembly { //solhint-disable-line + forwardedToBuffer := mload(add(code, 31)) + } + forwardedToBuffer &= (0x1 << 20 * 8) - 1; + forwardedTo = address(forwardedToBuffer >> ((48 - size) * 8)); + } + } + } +} +``` +The ContractProbe contract is deployed on Kovan at `0x8b98e65e0e8bce0f71a2a22f3d2666591e4cc857` and on Mainnet at `0x0c953133aa046965b83a3de1215ed4285414537c` ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From b2f2d48decceda7a597eda4e24262d8c62b27411 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Thu, 26 Jul 2018 12:28:44 -0700 Subject: [PATCH 067/177] Automatically merged updates to draft EIP(s) 1193 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1193.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md index 67792ef6..c60a99d9 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -12,7 +12,7 @@ requires: 1102 ## Summary -This EIP formalizes an Ethereum Provider JavaScript API to create consistency across clients. +This EIP formalizes an Ethereum Provider JavaScript API for consistency across clients and applications. The provider is designed to be minimal, containing 3 methods: `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`. @@ -369,7 +369,7 @@ class EthereumProvider extends EventEmitter { } else { promise.resolve(result); } - delete this.promises[id]; + delete this._promises[id]; } } else { if (method && method.indexOf('_subscription') > -1) { From fd0b4a8b358e2f859f4179cb3f3b9a56ca95247d Mon Sep 17 00:00:00 2001 From: Alan Lu Date: Thu, 26 Jul 2018 16:25:55 -0500 Subject: [PATCH 068/177] Automatically merged updates to draft EIP(s) 1154 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1154.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/EIPS/eip-1154.md b/EIPS/eip-1154.md index ad4437ec..51651576 100644 --- a/EIPS/eip-1154.md +++ b/EIPS/eip-1154.md @@ -42,6 +42,9 @@ Both the ID and the results are intentionally unstructured so that things like t
    Result
    Data associated with an id which is reported by an oracle. This data oftentimes will be the answer to a question tied to the id. Other equivalent terms that have been used include: answer, data, outcome.
    + +
    Report
    +
    A pair (ID, result) which an oracle sends to an oracle handler.
    ```solidity From 69c49b597a00853d092431522b3e3624acc283fa Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Fri, 27 Jul 2018 13:40:13 -0700 Subject: [PATCH 069/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 484 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 390 insertions(+), 94 deletions(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index d8b2df39..d9e001a9 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -95,7 +95,16 @@ Composable functions that make transfers follow the same parameter format: **fro For example the ` getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId)` composable function transfers an ERC721 token from an address to a top-down composable. The `_from` parameter is the **from**, the `_tokenId` parameter is the **to** and the `address _childContract, uint256 _childTokenId` parameters are the **what**. -The `transferChild/safeTransferChild` transfer functions do not have a **from** because it is already known that the **from** is `this`, the current contract. +Another example is the `safeTransferChild(uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId)` function. The `_fromTokenId` is the **from**, the `_to` is the **to** and the `address _childContract, address _childTokenId` parameters are the **what**. + +### transferFrom/safeTransferFrom Functions Do Not Transfer Tokens Owned By Tokens + +In bottom-up and top-down composable contracts the `transferFrom` and `safeTransferFrom` functions must throw if they attempt to transfer a token that is owned by another token. + +The reason for this is that these functions do not explicitly specify which token owns a token to be transferred. [See the rational section for more information about this.](#explicit-transfer-parameters) + +`transferFrom/safeTransferFrom` functions must be used to transfer tokens that are owned by an address. + ### ERC721 Top-Down Composable @@ -109,6 +118,15 @@ There are two ways to transfer a ERC721 token to a top-down composable: The first ways is for ERC721 contracts that have a `safeTransferFrom` function. The second way is for contracts that do not have this function such as cryptokitties. +Here is an example of transferring ERC721 token 3 from an address to top-down composable token 6: + +```solidity +uint256 tokenId = 6; +bytes memory tokenIdBytes = new bytes(32); +assembly { mstore(add(tokenIdBytes, 32), tokenId) } +ERC721(contractAddress).safeTransferFrom(userAddress, composableAddress, 3, tokenIdBytes); +``` + Every ERC721 top-down composable compliant contract must implement the ERC998ERC721TopDown interface. The ERC998ERC721TopDownEnumerable and ERC998ERC20TopDownEnumerable interfaces are optional. @@ -118,7 +136,7 @@ pragma solidity ^0.4.24; /// @title ERC998ERC721 Top-Down Composable Non-Fungible Token /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md -/// Note: the ERC-165 identifier for this interface is 0xe85c4c41 +/// Note: the ERC-165 identifier for this interface is 0x1bc995e4 interface ERC998ERC721TopDown { /// @dev This emits when a token receives a child token. @@ -143,65 +161,115 @@ interface ERC998ERC721TopDown { /// @notice Get the root owner of tokenId. /// @param _tokenId The token to query for a root owner address - /// @return rootOwner The root owner at the top of tree of tokens. + /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner); /// @notice Get the root owner of a child token. /// @param _childContract The contract address of the child token. /// @param _childTokenId The tokenId of the child. - /// @return rootOwner The root owner at the top of tree of tokens. - function rootOwnerOfChild(address _childContract, uint256 _childTokenId) public view returns (bytes32 rootOwner); + /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. + function rootOwnerOfChild( + address _childContract, + uint256 _childTokenId + ) + public + view + returns (bytes32 rootOwner); /// @notice Get the parent tokenId of a child token. /// @param _childContract The contract address of the child token. /// @param _childTokenId The tokenId of the child. - /// @return parentTokenOwner The parent address of the parent token + /// @return parentTokenOwner The parent address of the parent token and ERC998 magic value /// @return parentTokenId The parent tokenId of _tokenId - function ownerOfChild(address _childContract, uint256 _childTokenId) + function ownerOfChild( + address _childContract, + uint256 _childTokenId + ) external view - returns (address parentTokenOwner, uint256 parentTokenId); + returns ( + bytes32 parentTokenOwner, + uint256 parentTokenId + ); /// @notice A token receives a child token /// @param _operator The address that caused the transfer. /// @param _from The owner of the child token. /// @param _childTokenId The token that is being transferred to the parent. /// @param _data Up to the first 32 bytes contains an integer which is the receiving parent tokenId. - function onERC721Received(address _operator, address _from, uint256 _childTokenId, bytes _data) + function onERC721Received( + address _operator, + address _from, + uint256 _childTokenId, + bytes _data + ) external returns(bytes4); - - /// @notice Let's an owning contract know that a specified token is transferred out - /// @param _operator The address that caused the transfer. - /// @param _toContract The contract that receives the child token - /// @param _childTokenId The child tokenId that is being removed. - function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId) external; + + /// @notice Transfer child token from top-down composable to address. + /// @param _to The address that receives the child token + /// @param _childContract The ERC721 contract of the child token. + /// @param _childTokenId The tokenId of the token that is being transferred. + function transferChild( + address _to, + address _childContract, + uint256 _childTokenId + ) + external; /// @notice Transfer child token from top-down composable to address. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. - function transferChild(address _to, address _childContract, uint256 _childTokenId) external; - - /// @notice Transfer child token from top-down composable to address. - /// @param _to The address that receives the child token - /// @param _childContract The ERC721 contract of the child token. - /// @param _childTokenId The tokenId of the token that is being transferred. - function safeTransferChild(address _to, address _childContract, uint256 _childTokenId) external; + function safeTransferChild( + address _to, + address _childContract, + uint256 _childTokenId + ) + external; /// @notice Transfer child token from top-down composable to address. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. /// @param _data Additional data with no specified format - function safeTransferChild(address _to, address _childContract, uint256 _childTokenId, bytes _data) external; + function safeTransferChild( + address _to, + address _childContract, + uint256 _childTokenId, + bytes _data + ) + external; + + /// @notice Transfer bottom-up composable child token from top-down composable to other ERC721 token. + /// @param _fromTokenId The owning token to transfer from. + /// @param _toContract The ERC721 contract of the receiving token + /// @param _toToken The receiving token + /// @param _childContract The bottom-up composable contract of the child token. + /// @param _childTokenId The token that is being transferred. + /// @param _data Additional data with no specified format + function transferChildToParent( + uint256 _fromTokenId, + address _toContract, + uint256 _toTokenId, + address _childContract, + uint256 _childTokenId, + bytes _data + ) + external /// @notice Get a child token from an ERC721 contract. /// @param _from The address that owns the child token. /// @param _tokenId The token that becomes the parent owner /// @param _childContract The ERC721 contract of the child token /// @param _childTokenId The tokenId of the child token - function getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId) external; + function getChild( + address _from, + uint256 _tokenId, + address _childContract, + uint256 _childTokenId + ) + external; } ``` @@ -209,39 +277,49 @@ interface ERC998ERC721TopDown { ```solidity /// @notice Get the root owner of tokenId. /// @param _tokenId The token to query for a root owner address -/// @return rootOwner The root owner at the top of tree of tokens. +/// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner); ``` This function traverses token owners until the the root owner address of `_tokenId` is found. -The first 4 bytes of rootOwner contain the ERC998 magic value `0x62ff4869`. The last 20 bytes contain the root owner address. +The first 4 bytes of rootOwner contain the ERC998 magic value `0xcd740db5`. The last 20 bytes contain the root owner address. The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `rootOwnerOf` function. The magic value is used in such calls to ensure a valid return value is received. -If it is unknown whether a contract has the `rootOwnerOf` function then the magic value must be compared to `0x62ff4869`. +If it is unknown whether a contract has the `rootOwnerOf` function then the first four bytes of the `rootOwner` return value must be compared to `0xcd740db5`. -`0x62ff4869` is equal to: `this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector;` +`0xcd740db5` is equal to: +```solidity +this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ +this.tokenOwnerOf.selector ^ this.ownerOfChild.selector; +``` Here is an example of a value returned by `rootOwnerOf`. -`0x62ff48690000000000000000e5240103e1ff986a2c8ae6b6728ffe0d9a395c59` +`0xcd740db50000000000000000e5240103e1ff986a2c8ae6b6728ffe0d9a395c59` #### rootOwnerOfChild ```solidity /// @notice Get the root owner of a child token. /// @param _childContract The contract address of the child token. /// @param _childTokenId The tokenId of the child. -/// @return rootOwner The root owner at the top of tree of tokens. -function rootOwnerOfChild(address _childContract, uint256 _childTokenId) public view returns (bytes32 rootOwner); +/// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. +function rootOwnerOfChild( + address _childContract, + uint256 _childTokenId +) + public + view + returns (bytes32 rootOwner); ``` This function traverses token owners until the the root owner address of the supplied child token is found. -The first 4 bytes of rootOwner contain the ERC998 magic value `0x62ff4869`. The last 20 bytes contain the root owner address. +The first 4 bytes of rootOwner contain the ERC998 magic value `0xcd740db5`. The last 20 bytes contain the root owner address. The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `rootOwnerOf` function. The magic value is used in such calls to ensure a valid return value is received. -If it is unknown whether a contract has the `rootOwnerOf` function then the magic value must be compared to `0x62ff4869`. +If it is unknown whether a contract has the `rootOwnerOfChild` function then the first four bytes of the `rootOwner` return value must be compared to `0xcd740db5`. #### ownerOfChild @@ -249,16 +327,27 @@ If it is unknown whether a contract has the `rootOwnerOf` function then the magi /// @notice Get the parent tokenId of a child token. /// @param _childContract The contract address of the child token. /// @param _childTokenId The tokenId of the child. -/// @return parentTokenOwner The parent address of the parent token +/// @return parentTokenOwner The parent address of the parent token and ERC998 magic value /// @return parentTokenId The parent tokenId of _tokenId -function ownerOfChild(address _childContract, uint256 _childTokenId) +function ownerOfChild( + address _childContract, + uint256 _childTokenId +) external view - returns (address parentTokenOwner, uint256 parentTokenId); + returns ( + address parentTokenOwner, + uint256 parentTokenId + ); ``` This function is used to get the parent tokenId of a child token and get the owner address of the parent token. +The first 4 bytes of parentTokenOwner contain the ERC998 magic value `0xcd740db5`. The last 20 bytes contain the parent token owner address. + +The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `ownerOfChild` function. The magic value is used in such calls to ensure a valid return value is received. + +If it is unknown whether a contract has the `ownerOfChild` function then the first four bytes of the `parentTokenOwner` return value must be compared to `0xcd740db5`. #### onERC721Received ```solidity @@ -267,7 +356,12 @@ This function is used to get the parent tokenId of a child token and get the own /// @param _from The prior owner of the child token. /// @param _childTokenId The token that is being transferred to the parent. /// @param _data Up to the first 32 bytes contains an integer which is the receiving parent tokenId. -function onERC721Received(address _operator, address _from, uint256 _childTokenId, bytes _data) +function onERC721Received( + address _operator, + address _from, + uint256 _childTokenId, + bytes _data +) external returns(bytes4); ``` @@ -278,31 +372,26 @@ The `onERC721Received` function is how a top-down composable contract is notifie The return value for `onERC721Received` is the magic value `0x150b7a02` which is equal to `bytes4(keccak256(abi.encodePacked("onERC721Received(address,address,uint256,bytes)")))`. -#### onERC998Removed -```solidity -/// @notice Let's an owning contract know that a specified token is transferred out -/// @param The address that caused the transfer. -/// @param _toContract The contract that receives the child token -/// @param _childTokenId The child tokenId that is being removed. -function onERC998Removed(address _operator, address _toContract, uint256 _childTokenId) external; -``` - -Any composable or "composable aware" contract must call the `onERC998Removed` function within all `transferFrom/safeTransferFrom` functions on the contract that is losing ownership of an ERC721 token. It is used to notify top-down composables that they no longer have a child token and to remove it from any internal bookkeeping/tracking. - -Contracts that call `onERC998Removed` must not throw if the call fails because it is possible that a contract does not have the `onERC998Removed` function. - #### transferChild ```solidity /// @notice Transfer child token from top-down composable to address. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. -function transferChild(address _to, address _childContract, uint256 _childTokenId) external; +function transferChild( + address _to, + address _childContract, + uint256 _childTokenId +) + external; ``` This function authenticates `msg.sender` and transfers a child token from a top-down composable to a different address. -This function makes this call within it: `ERC721(_childContract).transferFrom(this, _to, _childTokenId);` +This function makes this call within it: +```solidity +ERC721(_childContract).transferFrom(this, _to, _childTokenId); +``` #### safeTransferChild ```solidity @@ -310,12 +399,20 @@ This function makes this call within it: `ERC721(_childContract).transferFrom(th /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. -function safeTransferChild(address _to, address _childContract, uint256 _childTokenId) external; +function safeTransferChild( + address _to, + address _childContract, + uint256 _childTokenId +) + external; ``` This function authenticates `msg.sender` and transfers a child token from a top-down composable to a different address. -This function makes this call within it: `ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId);` +This function makes this call within it: +```solidity +ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId); +``` #### safeTransferChild ```solidity @@ -324,14 +421,56 @@ This function makes this call within it: `ERC721(_childContract).safeTransferFro /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to -function safeTransferChild(address _to, address _childContract, uint256 _childTokenId, bytes _data) external; +function safeTransferChild( + address _to, + address _childContract, + uint256 _childTokenId, + bytes _data +) + external; ``` This function authenticates `msg.sender` and transfers a child token from a top-down composable to a different address or to a different top-down composable. A child token is transferred to a different top-down composable if the `_to` address is a top-down composable contract and `bytes _data` is supplied an integer representing the parent tokenId. -This function makes this call within it: `ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId, _data);` +This function makes this call within it: +```solidity +ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId, _data); +``` + +#### transferChildToParent +```solidity +/// @notice Transfer bottom-up composable child token from top-down composable to other ERC721 token. +/// @param _fromTokenId The owning token to transfer from. +/// @param _toContract The ERC721 contract of the receiving token +/// @param _toToken The receiving token +/// @param _childContract The bottom-up composable contract of the child token. +/// @param _childTokenId The token that is being transferred. +/// @param _data Additional data with no specified format +function transferChildToParent( + uint256 _fromTokenId, + address _toContract, + uint256 _toTokenId, + address _childContract, + uint256 _childTokenId, + bytes _data +) + external +``` + +This function authenticates `msg.sender` and transfers a child bottom-up composable token from a top-down composable to a different ERC721 token. This function can only be used when the child token is a bottom-up composable token. It is designed to transfer a bottom-up composable token from a top-down composable to an ERC721 token (bottom-up style) in one transaction. + +This function makes this call within it: +```solidity +ERC998ERC721BottomUp(_childContract).transferToParent( + address(this), + _toContract, + _toTokenId, + _childTokenId, + _data +); +``` #### getChild ```solidity @@ -340,7 +479,13 @@ This function makes this call within it: `ERC721(_childContract).safeTransferFro /// @param _tokenId The token that becomes the parent owner /// @param _childContract The ERC721 contract of the child token /// @param _childTokenId The tokenId of the child token -function getChild(address _from, uint256 _tokenId, address _childContract, uint256 _childTokenId) external; +function getChild( + address _from, + uint256 _tokenId, + address _childContract, + uint256 _childTokenId +) + external; ``` This function is used to transfer an ERC721 token when its contract does not have a `safeTransferChild(address _to, address _childContract, uint256 _childTokenId, bytes _data)` function. @@ -367,20 +512,36 @@ interface ERC998ERC721TopDownEnumerable { /// @param _tokenId The parent token of child tokens in child contract /// @param _index The index position of the child contract /// @return childContract The contract found at the tokenId and index. - function childContractByIndex(uint256 _tokenId, uint256 _index) external view returns (address childContract); + function childContractByIndex( + uint256 _tokenId, + uint256 _index + ) + external + view + returns (address childContract); /// @notice Get the total number of child tokens owned by tokenId that exist in a child contract. /// @param _tokenId The parent token of child tokens /// @param _childContract The child contract containing the child tokens /// @return uint256 The total number of child tokens found in child contract that are owned by tokenId. - function totalChildTokens(uint256 _tokenId, address _childContract) external view returns(uint256); + function totalChildTokens( + uint256 _tokenId, + address _childContract + ) + external + view + returns(uint256); /// @notice Get child token owned by tokenId, in child contract, at index position /// @param _tokenId The parent token of the child token /// @param _childContract The child contract of the child token /// @param _index The index position of the child token. /// @return childTokenId The child tokenId for the parent token, child token and index - function childTokenByIndex(uint256 _tokenId, address _childContract, uint256 _index) + function childTokenByIndex( + uint256 _tokenId, + address _childContract, + uint256 _index + ) external view returns (uint256 childTokenId); @@ -442,14 +603,26 @@ interface ERC998ERC20TopDown { /// @param _tokenId The token that owns the ERC20 tokens /// @param _erc20Contract The ERC20 contract /// @return The number of ERC20 tokens owned by a token from an ERC20 contract - function balanceOfERC20(uint256 _tokenId, address _erc20Contract) external view returns(uint256); + function balanceOfERC20( + uint256 _tokenId, + address _erc20Contract + ) + external + view + returns(uint256); /// @notice Transfer ERC20 tokens to address /// @param _tokenId The token to transfer from /// @param _value The address to send the ERC20 tokens to /// @param _erc20Contract The ERC20 contract /// @param _value The number of ERC20 tokens to transfer - function transferERC20(uint256 _tokenId, address _to, address _erc20Contract, uint256 _value) external; + function transferERC20( + uint256 _tokenId, + address _to, + address _erc20Contract, + uint256 _value + ) + external; /// @notice Transfer ERC20 tokens to address or ERC20 top-down composable /// @param _tokenId The token to transfer from @@ -457,7 +630,13 @@ interface ERC998ERC20TopDown { /// @param _erc223Contract The ERC223 token contract /// @param _value The number of ERC20 tokens to transfer /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to - function transferERC223(uint256 _tokenId, address _to, address _erc223Contract, uint256 _value, bytes _data) + function transferERC223( + uint256 _tokenId, + address _to, + address _erc223Contract, + uint256 _value, + bytes _data + ) external; /// @notice Get ERC20 tokens from ERC20 contract. @@ -465,7 +644,13 @@ interface ERC998ERC20TopDown { /// @param _tokenId The token to transfer the ERC20 tokens to. /// @param _erc20Contract The ERC20 token contract /// @param _value The number of ERC20 tokens to transfer - function getERC20(address _from, uint256 _tokenId, address _erc20Contract, uint256 _value) external; + function getERC20( + address _from, + uint256 _tokenId, + address _erc20Contract, + uint256 _value + ) + external; } ``` #### tokenFallback @@ -485,7 +670,13 @@ This function comes from the [ERC223 standard](https://github.com/ethereum/EIPs/ /// @param _tokenId The token that owns the ERC20 tokens /// @param _erc20Contract The ERC20 contract /// @return The number of ERC20 tokens owned by a token from an ERC20 contract -function balanceOfERC20(uint256 _tokenId, address _erc20Contract) external view returns(uint256); +function balanceOfERC20( + uint256 _tokenId, + address _erc20Contract +) + external + view + returns(uint256); ``` Gets the balance of ERC20 tokens owned by a token from a specific ERC20 contract. @@ -497,7 +688,13 @@ Gets the balance of ERC20 tokens owned by a token from a specific ERC20 contract /// @param _value The address to send the ERC20 tokens to /// @param _erc20Contract The ERC20 contract /// @param _value The number of ERC20 tokens to transfer -function transferERC20(uint256 _tokenId, address _to, address _erc20Contract, uint256 _value) external; +function transferERC20( + uint256 _tokenId, + address _to, + address _erc20Contract, + uint256 _value +) + external; ``` This is used to transfer ERC20 tokens from a token to an address. This function calls `ERC20(_erc20Contract).transfer(_to, _value)`; @@ -512,7 +709,13 @@ This function must authenticate `msg.sender`. /// @param _erc223Contract The ERC223 token contract /// @param _value The number of ERC20 tokens to transfer /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to - function transferERC223(uint256 _tokenId, address _to, address _erc223Contract, uint256 _value, bytes _data) + function transferERC223( + uint256 _tokenId, + address _to, + address _erc223Contract, + uint256 _value, + bytes _data + ) external; ``` @@ -527,7 +730,13 @@ This function must authenticate `msg.sender`. /// @param _tokenId The token to transfer the ERC20 tokens to. /// @param _erc20Contract The ERC20 token contract /// @param _value The number of ERC20 tokens to transfer -function getERC20(address _from, uint256 _tokenId, address _erc20Contract, uint256 _value) external; +function getERC20( + address _from, + uint256 _tokenId, + address _erc20Contract, + uint256 _value +) + external; ``` This function is used to transfer ERC20 tokens to an ERC20 top-down composable when an ERC20 contract does not have a `transferERC223(uint256 _tokenId, address _to, address _erc223Contract, uint256 _value, bytes _data)` function. @@ -552,7 +761,13 @@ interface ERC998ERC20TopDownEnumerable { /// @param _tokenId The token that owns ERC20 tokens. /// @param _index The index position of the ERC20 contract. /// @return address The ERC20 contract - function erc20ContractByIndex(uint256 _tokenId, uint256 _index) external view returns(address); + function erc20ContractByIndex( + uint256 _tokenId, + uint256 _index + ) + external + view + returns(address); } ``` @@ -574,8 +789,8 @@ interface ERC998ERC721BottomUp { /// @param _tokenId The token that is transferred event TransferToParent( address indexed _toContract, - uint256 indexed _toTokenId, - uint256 _tokenId + uint256 indexed _toTokenId, + uint256 _tokenId ); /// @dev This emits when a token is transferred from an ERC721 token @@ -584,31 +799,43 @@ interface ERC998ERC721BottomUp { /// @param _tokenId The token that is transferred event TransferFromParent( address indexed _fromContract, - uint256 indexed _fromTokenId, - uint256 _tokenId + uint256 indexed _fromTokenId, + uint256 _tokenId ); /// @notice Get the root owner of tokenId. /// @param _tokenId The token to query for a root owner address - /// @return rootOwner The root owner at the top of tree of tokens. + /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. function rootOwnerOf(uint256 _tokenId) external view returns (bytes rootOwner); /// @notice Get the owner addess and parent token (if there is one) of a token /// @param _tokenId The tokenId to query. /// @return tokenOwner The owner address of the token - /// @return parentTokenId The parent owner of the token + /// @return parentTokenId The parent owner of the token and ERC998 magic value /// @return isParent True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId - function tokenOwnerOf(uint256 _tokenId) + function tokenOwnerOf( + uint256 _tokenId + ) external view - returns (address tokenOwner, uint256 parentTokenId, bool isParent); + returns ( + bytes32 tokenOwner, + uint256 parentTokenId, + bool isParent + ); /// @notice Transfer token from owner address to a token /// @param _from The owner address /// @param _toContract The ERC721 contract of the receiving token /// @param _toToken The receiving token /// @param _data Additional data with no specified format - function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) + function transferToParent( + address _from, + address _toContract, + uint256 _toTokenId, + uint256 _tokenId, + bytes _data + ) external; /// @notice Transfer token from a token to an address @@ -617,7 +844,13 @@ interface ERC998ERC721BottomUp { /// @param _to The address the token is transferred to. /// @param _tokenId The token that is transferred /// @param _data Additional data with no specified format - function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _tokenId, bytes _data) + function transferFromParent( + address _fromContract, + uint256 _fromTokenId, + address _to, + uint256 _tokenId, + bytes _data + ) external; /// @notice Transfer a token from a token to another token @@ -627,7 +860,14 @@ interface ERC998ERC721BottomUp { /// @param _toToken The receiving token /// @param _tokenId The token that is transferred /// @param _data Additional data with no specified format - function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) + function transferAsChild( + address _fromContract, + uint256 _fromTokenId, + address _toContract, + uint256 _toTokenId, + uint256 _tokenId, + bytes _data + ) external; } ``` @@ -636,40 +876,57 @@ interface ERC998ERC721BottomUp { ```solidity /// @notice Get the root owner of tokenId. /// @param _tokenId The token to query for a root owner address -/// @return rootOwner The root owner at the top of tree of tokens. +/// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. function rootOwnerOf(uint256 _tokenId) public view returns (bytes32 rootOwner); ``` This function traverses token owners until the the root owner address of `_tokenId` is found. -The first 4 bytes of rootOwner contain the ERC998 magic value `0x62ff4869`. The last 20 bytes contain the root owner address. +The first 4 bytes of rootOwner contain the ERC998 magic value `0xcd740db5`. The last 20 bytes contain the root owner address. The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `rootOwnerOf` function. The magic value is used in such calls to ensure a valid return value is received. -If it is unknown whether a contract has the `rootOwnerOf` function then the magic value must be compared to `0x62ff4869`. +If it is unknown whether a contract has the `rootOwnerOf` function then the first four bytes of the `rootOwner` return value must be compared to `0xcd740db5`. -`0x62ff4869` is equal to: `this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector;` +`0xcd740db5` is equal to: +```solidity +this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ +this.tokenOwnerOf.selector ^ this.ownerOfChild.selector; +``` Here is an example of a value returned by `rootOwnerOf`. -`0x62ff48690000000000000000e5240103e1ff986a2c8ae6b6728ffe0d9a395c59` +`0xcd740db50000000000000000e5240103e1ff986a2c8ae6b6728ffe0d9a395c59` #### tokenOwnerOf ```solidity /// @notice Get the owner addess and parent token (if there is one) of a token /// @param _tokenId The tokenId to query. -/// @return tokenOwner The owner address of the token +/// @return tokenOwner The owner address of the token and ERC998 magic value. /// @return parentTokenId The parent owner of the token /// @return isParent True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId -function tokenOwnerOf(uint256 _tokenId) +function tokenOwnerOf( + uint256 _tokenId +) external view - returns (address tokenOwner, uint256 parentTokenId, bool isParent); + returns ( + bytes32 tokenOwner, + uint256 parentTokenId, + bool isParent + ); ``` This function is used to get the owning address and parent tokenId of a token if there is one stored in the contract. If `isParent` is true then `tokenOwner` is the owning ERC721 contract address and `parentTokenId` is a valid parent tokenId. If `isParent` is false then `tokenOwner` is a user address and `parentTokenId` does not contain a valid parent tokenId and must be ignored. +The first 4 bytes of `tokenOwner` contain the ERC998 magic value `0xcd740db5`. The last 20 bytes contain the token owner address. + +The magic value is returned because this function may be called on contracts when it is unknown if the contracts have a `tokenOwnerOf` function. The magic value is used in such calls to ensure a valid return value is received. + +If it is unknown whether a contract has the `rootOwnerOf` function then the first four bytes of the `tokenOwner` return value must be compared to `0xcd740db5`. + + #### transferToParent ```solidity /// @notice Transfer token from owner address to a token @@ -677,7 +934,13 @@ If `isParent` is true then `tokenOwner` is the owning ERC721 contract address an /// @param _toContract The ERC721 contract of the receiving token /// @param _toToken The receiving token /// @param _data Additional data with no specified format -function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) +function transferToParent( + address _from, + address _toContract, + uint256 _toTokenId, + uint256 _tokenId, + bytes _data +) external; ``` @@ -694,7 +957,13 @@ This function must check that `_toToken` exists in `_toContract` and throw if no /// @param _to The address the token is transferred to. /// @param _tokenId The token that is transferred /// @param _data Additional data with no specified format -function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _tokenId, bytes _data) +function transferFromParent( + address _fromContract, + uint256 _fromTokenId, + address _to, + uint256 _tokenId, + bytes _data +) external; ``` This function is used to transfer a token from a token to an address. `msg.sender` must be authenticated. @@ -710,7 +979,14 @@ This function must check that `_fromContract` and `_fromTokenId` own `_tokenId` /// @param _toToken The receiving token /// @param _tokenId The token that is transferred /// @param _data Additional data with no specified format -function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _tokenId, bytes _data) +function transferAsChild( + address _fromContract, + uint256 _fromTokenId, + address _toContract, + uint256 _toTokenId, + uint256 _tokenId, + bytes _data +) external; ``` @@ -731,14 +1007,24 @@ interface ERC998ERC721BottomUpEnumerable { /// @param _parentContract The contract the parent ERC721 token is from. /// @param _parentTokenId The parent tokenId that owns tokens // @return uint256 The number of ERC721 tokens owned by parent token. - function totalChildTokens(address _parentContract, uint256 _parentTokenId) external view returns (uint256); + function totalChildTokens( + address _parentContract, + uint256 _parentTokenId + ) + external + view + returns (uint256); /// @notice Get a child token by index /// @param _parentContract The contract the parent ERC721 token is from. /// @param _parentTokenId The parent tokenId that owns the token /// @param _index The index position of the child token /// @return uint256 The child tokenId owned by the parent token - function childTokenByIndex(address _parentContract, uint256 _parentTokenId, uint256 _index) + function childTokenByIndex( + address _parentContract, + uint256 _parentTokenId, + uint256 _index + ) external view returns (uint256); @@ -748,12 +1034,22 @@ interface ERC998ERC721BottomUpEnumerable { ## Rationale Two different kinds of composable (top-down and bottom-up) exist to handle different use cases. A regular ERC721 token cannot own a top-down composable, but it can own a bottom-up composable. A bottom-up composable cannot own a regular ERC721 but a top-down composable can own a regular ERC721 token. Having multiple kinds of composables enable different token ownership possibilities. -#### Which Kind of Composable To Use? +### Which Kind of Composable To Use? If you want to transfer regular ERC721 tokens to non-fungible tokens, then use top-down composables. If you want to transfer non-fungible tokens to regular ERC721 tokens then use bottom-up composables. +### Explicit Transfer Parameters + +Every ERC998 transfer function includes explicit parameters to specify the prior owner and the new owner of a token. Explicitly providing **from** and **to** is done intentionally to avoid situations where tokens are transferred in unintended ways. + +Here is an example of what could occur if **from** was not explicitly provided in transfer functions: +> An exchange contract is an approved operator in a specific composable contract for user A, user B and user C. +> +> User A transfers token 1 to user B. At the same time the exchange contract transfers token 1 to user C (with the implicit intention to transfer from user A). User B gets token 1 for a minute before it gets incorrectly transferred to user C. The second transfer should have failed but it didn't because no explicit **from** was provided to ensure that token 1 came from user A. + + ### Additional Reading Material [Top-Down and Bottom-Up Composables, What's the Difference and Which One Should You Use?](https://hackernoon.com/top-down-and-bottom-up-composables-whats-the-difference-and-which-one-should-you-use-db939f6acf1d) From 67038a4788b826a10a372652d503cbb5682f278a Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Fri, 27 Jul 2018 14:10:40 -0700 Subject: [PATCH 070/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index d9e001a9..18bd6b89 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -87,8 +87,6 @@ Top-down and bottom-up composables are interoperable with each other. It is poss Tokens/contracts that implement the above authentication and traversal functionality are "composable aware". -Composables and "composable aware" contracts/tokens must call the `onERC998Removed` function in all their transferFrom/safeTransferFrom functions to notify owning/_from contracts that ERC721 tokens are removed. - ### Composable Transfer Function Parameter Format Composable functions that make transfers follow the same parameter format: **from:to:what**. @@ -99,7 +97,7 @@ Another example is the `safeTransferChild(uint256 _fromTokenId, address _to, add ### transferFrom/safeTransferFrom Functions Do Not Transfer Tokens Owned By Tokens -In bottom-up and top-down composable contracts the `transferFrom` and `safeTransferFrom` functions must throw if they attempt to transfer a token that is owned by another token. +In bottom-up and top-down composable contracts the `transferFrom` and `safeTransferFrom` functions must throw if they are called directly to transfer a token that is owned by another token. The reason for this is that these functions do not explicitly specify which token owns a token to be transferred. [See the rational section for more information about this.](#explicit-transfer-parameters) @@ -136,7 +134,7 @@ pragma solidity ^0.4.24; /// @title ERC998ERC721 Top-Down Composable Non-Fungible Token /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md -/// Note: the ERC-165 identifier for this interface is 0x1bc995e4 +/// Note: the ERC-165 identifier for this interface is 0x1efdf36a interface ERC998ERC721TopDown { /// @dev This emits when a token receives a child token. From 5bddef7c7ab9f1c0c4895e5adbbf0ff105b709e6 Mon Sep 17 00:00:00 2001 From: Micah Zoltu Date: Sat, 28 Jul 2018 19:52:22 +0800 Subject: [PATCH 072/177] Automatically merged updates to draft EIP(s) 234 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-234.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/EIPS/eip-234.md b/EIPS/eip-234.md index 5707d483..63b18fd7 100644 --- a/EIPS/eip-234.md +++ b/EIPS/eip-234.md @@ -40,9 +40,7 @@ The only potential issue here is the `fromBlock` and `toBlock` fields. It would ## Implementation -- [ ] Geth -- [ ] Parity -- [ ] EthereumJ +- [x] Geth ## Copyright From 4eadc60b7ee1aa5c89e6a6235ae590cb9bc74ee6 Mon Sep 17 00:00:00 2001 From: Micah Zoltu Date: Sat, 28 Jul 2018 19:59:25 +0800 Subject: [PATCH 073/177] Automatically merged updates to draft EIP(s) 234 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-234.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-234.md b/EIPS/eip-234.md index 63b18fd7..06f99c5e 100644 --- a/EIPS/eip-234.md +++ b/EIPS/eip-234.md @@ -40,7 +40,7 @@ The only potential issue here is the `fromBlock` and `toBlock` fields. It would ## Implementation -- [x] Geth +- [x] [Geth](https://github.com/ethereum/go-ethereum/pull/16734) ## Copyright From 4c9c115fd7d8c9a60568d04644cb7f3151b97eda Mon Sep 17 00:00:00 2001 From: Afri Schoedon <5chdn@users.noreply.github.com> Date: Sun, 29 Jul 2018 16:16:56 +0200 Subject: [PATCH 074/177] Add EIPs to eip-1013.md (#1219) --- EIPS/eip-1013.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/EIPS/eip-1013.md b/EIPS/eip-1013.md index e05f9763..63de5905 100644 --- a/EIPS/eip-1013.md +++ b/EIPS/eip-1013.md @@ -5,28 +5,31 @@ author: Nick Savers (@nicksavers) type: Meta status: Draft created: 2018-04-20 -requires: 145, 210 +requires: 145, 210, 1014, 1052, 1087 --- ## Abstract -This specifies the changes included in the hard fork named Constantinople. +This meta-EIP specifies the changes included in the Ethereum hardfork named Constantinople. ## Specification - Codename: Constantinople - Aliases: Metropolis/Constantinople, Metropolis part 2 - Activation: - - Block >= TBD on Mainnet - - Block >= TBD on Ropsten testnet + - `Block >= TBD` on the Ethereum mainnet + - `Block >= TBD` on the Ropsten testnet - Included EIPs: - - [EIP 145](./eip-145.md) (Bitwise shifting instructions in EVM) - - [EIP 210](./eip-210.md) (Blockhash refactoring) - - ... + - [EIP 145](./eip-145.md): Bitwise shifting instructions in EVM + - [EIP 210](./eip-210.md): Blockhash refactoring + - [EIP 1014](./eip-1014.md): Skinny CREATE2 + - [EIP 1052](./eip-1052.md): EXTCODEHASH Opcode + - [EIP 1087](./eip-1087.md): Net gas metering for SSTORE operations + - TBD: Ice-age delay ## References -1. Links to Ethereum blog announcement and others +The list above includes the EIPs discussed as candidates for Constantinople at the [All Core Dev Meeting #42](https://github.com/ethereum/pm/blob/c78d7ff147daaafa4dce60ba56cdedbe268a0488/All%20Core%20Devs%20Meetings/Meeting%2042.md#constantinople-hard-fork-and-timing). Final decision is planned for the subsequent meeting #43. ## Copyright From 7c93c5aefb7a107047f2d4c66d87b37967d43103 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Sun, 29 Jul 2018 12:10:56 -0700 Subject: [PATCH 075/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 167 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index 18bd6b89..e1448797 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -1,7 +1,7 @@ --- eip: 998 title: ERC-998 Composable Non-Fungible Token Standard -author: Matt Lockyer , Nick Mudge +author: Matt Lockyer , Nick Mudge , Jordan Schalm discussions-to: https://github.com/ethereum/EIPs/issues/998 type: Standards Track category: ERC @@ -35,6 +35,7 @@ This specification specifies: 1. [ERC721 top-down composable tokens that receive, hold and transfer ERC721 tokens](#erc721-top-down-composable) 2. [ERC20 top-down composable tokens that receive, hold and transfer ERC20 tokens](#erc20-top-down-composable) 3. [ERC721 bottom-up composable tokens that attach themselves to other ERC721 tokens.](#erc721-bottom-up-composable) +4. [ERC20 bottom-up composable tokens that attach themselves to other ERC20 tokens.](#erc20-bottom-up-composable) ### ERC721 @@ -1029,6 +1030,170 @@ interface ERC998ERC721BottomUpEnumerable { } ``` +### ERC20 Bottom-Up Composable + +ERC20 bottom-up composables are ERC20 tokens that attach themselves to ERC721 tokens, or are owned by a user address like standard ERC20 tokens. + +When owned by an ERC721 token, ERC20 bottom-up composable contracts store the owning address of a token and the parent tokenId. ERC20 bottom-up composables add several methods to the ERC20 and ERC223 interfaces allowing for querying the balance of parent tokens, and transferring tokens to, from, and between parent tokens. + +This functionality can be implemented by adding one additional mapping to track balances of tokens, in addition to the standard mapping for tracking user address balances. +```solidity +/// @dev This mapping tracks standard ERC20/ERC223 ownership, where an address owns +/// a particular amount of tokens. +mapping(address => uint) userBalances; + +/// @dev This additional mapping tracks ERC998 ownership, where an ERC721 token owns +/// a particular amount of tokens. This tracks contractAddres => tokenId => balance +mapping(address => mapping(uint => uint)) nftBalances; +``` + +The complete interface is below. + +```solidity +/// @title ERC998ERC20 Bottom-Up Composable Fungible Token +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md +/// Note: The ERC-165 identifier for this interface is 0xffafa991 +interface ERC998ERC20BottomUp { + + /// @dev This emits when a token is transferred to an ERC721 token + /// @param _toContract The contract the token is transferred to + /// @param _toTokenId The token the token is transferred to + /// @param _amount The amount of tokens transferred + event TransferToParent( + address indexed _toContract, + uint256 indexed _toTokenId, + uint256 _amount + ); + + /// @dev This emits when a token is transferred from an ERC721 token + /// @param _fromContract The contract the token is transferred from + /// @param _fromTokenId The token the token is transferred from + /// @param _amount The amount of tokens transferred + event TransferFromParent( + address indexed _fromContract, + uint256 indexed _fromTokenId, + uint256 _amount + ); + + /// @notice Get the balance of a non-fungible parent token + /// @param _tokenContract The contract tracking the parent token + /// @param _tokenId The ID of the parent token + /// @return amount The balance of the token + function balanceOfToken(address _tokenContract, uint256 _tokenId) + external + view + returns (uint256 amount); + + /// @notice Transfer tokens from owner address to a token + /// @param _from The owner address + /// @param _toContract The ERC721 contract of the receiving token + /// @param _toToken The receiving token + /// @param _amount The amount of tokens to transfer + function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _amount) + external; + + /// @notice Transfer token from a token to an address + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _to The address the token is transferred to + /// @param _amount The amount of tokens to transfer + function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount) + external; + + /// @notice Transfer token from a token to an address, using ERC223 semantics + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _to The address the token is transferred to + /// @param _amount The amount of tokens to transfer + /// @param _data Additional data with no specified format, can be used to specify the sender tokenId + function transferFromParentERC223(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount, bytes _data) + external; + + /// @notice Transfer a token from a token to another token + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _toContract The ERC721 contract of the receiving token + /// @param _toToken The receiving token + /// @param _amount The amount tokens to transfer + function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _amount) + external; +} +``` + +#### balanceOfToken +```solidity +/// @notice Get the balance of a non-fungible parent token +/// @param _tokenContract The contract tracking the parent token +/// @param _tokenId The ID of the parent token +/// @return amount The balance of the token +function balanceOfToken(address _tokenContract, uint256 _tokenId) + external + view + returns (uint256 amount); +``` + +This function returns the balance of a non-fungible token. It mirrors the standard ERC20 method `balanceOf`, but accepts the address of the parent token's contract, and the parent token's ID. This method behaves identically to `balanceOf`, but checks for ownership by ERC721 tokens rather than user addresses. + +#### transferToParent +```solidity +/// @notice Transfer tokens from owner address to a token +/// @param _from The owner address +/// @param _toContract The ERC721 contract of the receiving token +/// @param _toToken The receiving token +/// @param _amount The amount of tokens to transfer +function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _amount) + external; +``` + +This function transfers an amount of tokens from a user address to an ERC721 token. This function MUST ensure that the recipient contract implements ERC721 using the ERC165 `supportsInterface` function. This function SHOULD ensure that the recipient token actually exists, by calling `ownerOf` on the recipient token's contract, and ensuring it neither throws nor returns the zero address. This function MUST emit the `TransferToParent` event upon a successful transfer (in addition to the standard ERC20 `Transfer` event!). This function MUST throw if the `_from` account balance does not have enough tokens to spend. + +#### transferFromParent +``` +solidity +/// @notice Transfer token from a token to an address +/// @param _fromContract The address of the owning contract +/// @param _fromTokenId The owning token +/// @param _to The address the token is transferred to +/// @param _amount The amount of tokens to transfer +function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount) + external; +``` + +This function transfers an amount of tokens from an ERC721 token to an address. This function MUST emit the `TransferFromParent` event upon a successful transfer (in addition to the standard ERC20 `Transfer` event!). This function MUST throw if the balance of the sender ERC721 token is less than the `_amount` specified. This function MUST verify that the `msg.sender` owns the sender ERC721 token, and MUST throw otherwise. + +#### transferFromParentERC223 +```solidity +/// @notice Transfer token from a token to an address, using ERC223 semantics +/// @param _fromContract The address of the owning contract +/// @param _fromTokenId The owning token +/// @param _to The address the token is transferred to +/// @param _amount The amount of tokens to transfer +/// @param _data Additional data with no specified format, can be used to specify the sender tokenId +function transferFromParentERC223(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount, bytes _data) + external; +``` + +This function transfers an amount of tokens from an ERC721 token to an address. This function has identical requirements to `transferFromParent`, except that it additionally MUST invoke `tokenFallback` on the recipient address, if the address is a contract, as specified by ERC223. + +### transferAsChild +```solidity +/// @notice Transfer a token from a token to another token +/// @param _fromContract The address of the owning contract +/// @param _fromTokenId The owning token +/// @param _toContract The ERC721 contract of the receiving token +/// @param _toToken The receiving token +/// @param _amount The amount tokens to transfer +function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _amount) + external; +``` + +This function transfers an amount of tokens from an ERC721 token to another ERC721 token. This function MUST emit BOTH the `TransferFromParent` and `TransferToParent` events (in addition to the standard ERC20 `Transfer` event!). This function MUST throw if the balance of the sender ERC721 token is less than the `_amount` specified. This function MUST verify that the `msg.sender` owns the sender ERC721 token, and MUST throw otherwise. This function MUST ensure that the recipient contract implements ERC721 using the ERC165 `supportsInterface` function. This function SHOULD ensure that the recipient token actually exists, by calling `ownerOf` on the recipient token's contract, and ensuring it neither throws nor returns the zero address. + +### Notes +For backwards-compatibility, implementations MUST emit the standard ERC20 `Transfer` event when a transfer occurs, regardless of whether the sender and recipient are addresses or ERC721 tokens. In the case that either sender or recipient are tokens, the corresponding parameter in the `Transfer` event SHOULD be the contract address of the token. + +Implementations MUST implement all ERC20 and ERC223 functions in addition to the functions specified in this interface. + ## Rationale Two different kinds of composable (top-down and bottom-up) exist to handle different use cases. A regular ERC721 token cannot own a top-down composable, but it can own a bottom-up composable. A bottom-up composable cannot own a regular ERC721 but a top-down composable can own a regular ERC721 token. Having multiple kinds of composables enable different token ownership possibilities. From d8714503751d3e6633e1a37d4965c4872fd7754b Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Mon, 30 Jul 2018 22:29:38 +0200 Subject: [PATCH 076/177] Automatically merged updates to draft EIP(s) 777 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-777.md | 318 ++++++++++++++++++++++++++---------------------- 1 file changed, 170 insertions(+), 148 deletions(-) diff --git a/EIPS/eip-777.md b/EIPS/eip-777.md index 08bedd57..e2177f77 100644 --- a/EIPS/eip-777.md +++ b/EIPS/eip-777.md @@ -12,9 +12,9 @@ requires: 820 ## Simple Summary -This EIP defines a standard interface for token contracts. +This EIP defines standard interfaces and behaviors for token contracts. -*The repository containing the reference implementation for this standard can be found at [jacquesd/ERC777] and installed via npm with: `npm install erc777`.* +*The repository containing the reference implementation for this standard can be found at [github.com/jacquesd/ERC777][jacquesd/ERC777] and installed via npm with `npm install erc777`.* ## Abstract @@ -29,11 +29,11 @@ It takes advantage of [ERC820] to find out whether and where to notify contracts This standard tries to improve the widely used [ERC20] token standard. The main advantages of this standard are: 1. Uses the same philosophy as Ether in that tokens are sent with `send(dest, value, data)`. -2. Both contracts and regular address can control and reject which token they send by registering a `tokensToSend` hook—rejection is done by throwing in the hook function. -3. Both contracts and regular addresses can control and reject which token they receive by registering a `tokensReceived` hook—rejection is done by throwing in the hook function. -4. The `tokensReceived` hook also avoids the double call needed in the [ERC20] standard (`approve` / `transferFrom`) to send tokens to a contract. -5. The token holder can "authorize" and "revoke" operators who can send tokens on their behalf. These operators are intended to be verified contracts such as an exchange, a cheque processor or an automatic charging system. -6. Every token transaction contains a `data` bytes field and a similar `operatorData`—in case of an operator transaction—to be used freely by the token holder and the operator respectively to pass data to the recipient. +2. Both contracts and regular addresses can control and reject which token they send by registering a `tokensToSend` hook. (Rejection is done by throwing in the hook function.) +3. Both contracts and regular addresses can control and reject which token they receive by registering a `tokensReceived` hook. (Rejection is done by throwing in the hook function.) +4. The `tokensReceived` hook also avoids the double call needed in the [ERC20] standard (`approve`/`transferFrom`) to send tokens to a contract. +5. The token holder can "authorize" and "revoke" operators which can send tokens on their behalf. These operators are intended to be verified contracts such as an exchange, a cheque processor or an automatic charging system. +6. Every token transaction contains a `data` bytes field and a similar `operatorData` to be used freely by the token holder and the operator respectively to pass data to the recipient. 7. It is backward compatible with wallets that do not contain the `tokensReceived` hook function by deploying a proxy contract implementing the `tokensReceived` hook for the wallet. ## Specification @@ -73,11 +73,11 @@ interface ERC777Token { event RevokedOperator(address indexed operator, address indexed tokenHolder); } ``` -The token-contract MUST implement the above interface. The implementation MUST follow the specifications described below. +The token contract MUST implement the above interface. The implementation MUST follow the specifications described below. -The token-contract MUST register the `ERC777Token` interface with its own address via [ERC820]. If the contract has a switch to enable or disable [ERC777] functions, every time the switch is triggered, the token MUST register or unregister the `ERC777Token` interface for its own address accordingly via [ERC820]. (Unregistering implies setting the address to `0x0`.) +The token contract MUST register the `ERC777Token` interface with its own address via [ERC820]. If the contract has a switch to enable or disable [ERC777] functions, every time the switch is triggered, the token MUST register or unregister the `ERC777Token` interface for its own address accordingly via [ERC820]. (Unregistering implies setting the address to `0x0`.) -The basic unit token MUST be 1018 (i.e. [ERC20]'s `decimals` MUST be `18`). All functions MUST consider any amount of tokens (such as balances and amount to send, mint, or burn) in the basic unit. +The basic unit token MUST be 1018 (i.e., [ERC20]'s `decimals` MUST be `18`). All functions MUST consider any amount of tokens (such as balances and amount to send, mint, or burn) in the basic unit. #### **View Functions** @@ -89,7 +89,7 @@ The `view` functions detailed below MUST be implemented. function name() public view returns (string) ``` -Returns the name of the token—e.g. `"MyToken"`. +Returns the name of the token, e.g., `"MyToken"`. > **returns:** Name of the token @@ -99,7 +99,7 @@ Returns the name of the token—e.g. `"MyToken"`. function symbol() public view returns (string) ``` -Returns the symbol of the token—e.g. `"MYT"`. +Returns the symbol of the token, e.g., `"MYT"`. > **returns:** Symbol of the token @@ -111,7 +111,7 @@ function totalSupply() public view returns (uint256) Get the total number of minted tokens. -The total supply MUST be equal to the sum of the balances as returned by `balanceOf` for all the addresses which appeared at least once in any of the `Sent`, `Minted` and `Burned` events. +The total supply MUST be equal to the sum of the balances of all addresses—as returned by the `balanceOf` function. > **returns:** Total supply of tokens currently in circulation. @@ -123,7 +123,7 @@ function balanceOf(address tokenHolder) public view returns (uint256) Get the balance of the account with address `tokenHolder`. -The balance MUST be zero (`0`) or greater. +The balance MUST be zero (`0`) or higher. > **parameters** > `tokenHolder`: Address for which the balance is returned @@ -138,21 +138,22 @@ function granularity() public view returns (uint256) Get the smallest part of the token that's not divisible. -The following rules MUST be applied with respect to the *granularity*: +The following rules MUST be applied regarding the *granularity*: -- The *granularity* value MUST NOT be changed. +- The *granularity* value MUST be at creation time. +- The *granularity* value MUST NOT be changed ever. - The *granularity* value MUST be greater or equal to `1`. - Any minting, send or burning of tokens MUST be a multiple of the *granularity* value. -- Any operation that would result in a balance that's not a multiple of the *granularity* value, MUST be considered invalid and the transaction MUST throw. +- Any operation that would result in a balance that's not a multiple of the *granularity* value MUST be considered invalid, and the transaction MUST throw. -*NOTE*: Most of the tokens SHOULD be fully partitionable, i.e. this function SHOULD return `1` unless there is a good reason for not allowing any partition of the token. +*NOTE*: Most of the tokens SHOULD be fully partitionable. I.e., this function SHOULD return `1` unless there is a good reason for not allowing any partition of the token. > **returns:** The smallest non-divisible part of the token. -*NOTE*: [`defaultOperators`][defaultOperators] and [`isOperatorFor`][isOperatorFor] are also `view` functions. They are defined under the [operators] for consistency. +*NOTE*: [`defaultOperators`][defaultOperators] and [`isOperatorFor`][isOperatorFor] are also `view` functions, defined under the [operators] for consistency. *[ERC20] compatibility requirement*: -The decimals of the token MUST always be `18`. For a *pure* ERC777 token the [ERC20] `decimal` function is OPTIONAL and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of `18` is implied.) For an [ERC20] enabled token, the `decimal` function is REQUIRED and MUST return `18`. +The decimals of the token MUST always be `18`. For a *pure* ERC777 token the [ERC20] `decimal` function is OPTIONAL, and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of `18` is implied.) For an [ERC20] enabled token, the `decimal` function is REQUIRED and MUST return `18`. *[ERC20] compatibility requirement*: The `name`, `symbol`, `totalSupply`, and `balanceOf` `view` functions MUST be backward compatible with [ERC20]. @@ -161,26 +162,29 @@ The `name`, `symbol`, `totalSupply`, and `balanceOf` `view` functions MUST be ba An `operator` is an address which is allowed to send and burn tokens on behalf of another address. -When an address becomes an *operator* for a *token holder*, an `AuthorizedOperator` event MUST be fired. The `AuthorizedOperator`'s `operator` (topic 1) and `tokenHolder` (topic 2) MUST be the addresses of the *operator* and the *token holder* respectively. +When an address becomes an *operator* for a *token holder*, an `AuthorizedOperator` event MUST be emitted. The `AuthorizedOperator`'s `operator` (topic 1) and `tokenHolder` (topic 2) MUST be the addresses of the *operator* and the *token holder* respectively. -When the *operator* of a *token holder* is revoked, a `RevokedOperator` event MUST be fired. The `RevokedOperator`'s `operator` (topic 1) and `tokenHolder` (topic 2) MUST be the addresses of the *operator* and the *token holder* respectively. +When a *token holder* revokes an *operator*, a `RevokedOperator` event MUST be emitted. The `RevokedOperator`'s `operator` (topic 1) and `tokenHolder` (topic 2) MUST be the addresses of the *operator* and the *token holder* respectively. -*NOTE*: A *token holder* CAN have multiple *operators* at the same time. +*NOTE*: A *token holder* MAY have multiple *operators* at the same time. -In addition, the token MAY define *default operators*. A *default operator* is an *operator* which is implicitly authorized for all *token holders*. `AuthorizedOperator` events MUST NOT be fired when defining the *default operators*. The rules below apply to *default operators*: +The token MAY define *default operators*. A *default operator* is an implicitly authorized *operator* for all *token holders*. `AuthorizedOperator` events MUST NOT be emitted when defining the *default operators*. The rules below apply to *default operators*: - The token contract MUST define *default operators* at creation time. -- The *default operators* MUST be invariants. I.e. the token contract MUST NOT add or remove *default operators* ever. -- `AuthorizedOperator` events MUST NOT be fired when defining *default operators*. -- A *token holder* MUST be allowed revoke a *default operator* (unless the *token holder* is a *default operator*). -- A *token holder* MUST be allowed to re-authorize a previously revoked *default operator* -- When a *default operator* is explicitly authorized or revoked for a specific *token holder*, an `AuthorizedOperator` or `RevokedOperator` event (respectively) MUST be fire. +- The *default operators* MUST be invariants. I.e., the token contract MUST NOT add or remove *default operators* ever. +- `AuthorizedOperator` events MUST NOT be emitted when defining *default operators*. +- A *token holder* MUST be allowed revoke a *default operator* (unless the *token holder* is the *default operator* in question). +- A *token holder* MUST be allowed to re-authorize a previously revoked *default operator*. +- When a *default operator* is explicitly authorized or revoked for a specific *token holder*, an `AuthorizedOperator` or `RevokedOperator` event (respectively) MUST be emitted. The following rules apply to any *operator*: - An address MUST always be an *operator* for itself. Hence an address MUST NOT ever be revoked as its own *operator*. -- If an address is an *operator* for a token holder `isOperatorFor` MUST return `true`. -- If an address is not an *operator* for a token holder `isOperatorFor` MUST return `false`. +- If an address is an *operator* for a *token holder*, `isOperatorFor` MUST return `true`. +- If an address is not an *operator* for a *token holder*, `isOperatorFor` MUST return `false`. +- The *operator* MUST pass the `data` from a *token holder* as is when sending and burning tokens. If the *token holder* does not provide any `data`, the operator MUST pass either an empty `data` field or MUST NOT pass any data (depending on whether the function requires the *token holder*'s `data` or not). + +*NOTE*: It is not expected for the token contract to validate the origin of the `data` field when the *operator* and *token holder* are different. However, the *operator* MUST not pass any information in the `data` field which does not originate from the *token holder*. The `defaultOperators`, `authorizeOperator`, `revokeOperator` and `isOperatorFor` functions described below MUST be implemented to manage *operators*. Token contracts MAY implement other functions to manage *operators*. @@ -191,7 +195,9 @@ Token contracts MAY implement other functions to manage *operators*. function defaultOperators() public view returns (address[]) ``` -Get the list of *default operators* as defined by the token contract +Get the list of *default operators* as defined by the token contract. + +*NOTE*: If the token contract does not have any *default operators*, this function MUST return an empty list. > **returns:** List of addresses of all the *default operators* @@ -203,11 +209,11 @@ function authorizeOperator(address operator) public Set a third party `operator` address as an *operator* of `msg.sender` to send, burn, and mint tokens on its behalf. -An `AuthorizedOperator` event MUST be fired on a successful call to this function. +An `AuthorizedOperator` event MUST be emitted on a successful call to this function. -*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to set the token holder (`msg.sender`) as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`). +*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to authorize the token holder (`msg.sender`) as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`). -*NOTE*: A token holder CAN authorize multiple *operators* at the same time. +*NOTE*: A token holder MAY authorize multiple *operators* at the same time. > **parameters** > `operator`: Address to set as an *operator* for `msg.sender`. @@ -221,11 +227,11 @@ function revokeOperator(address operator) public Remove the right of `operator` address to be an *operator* for `msg.sender` and to send and burn tokens on its behalf. -Revoke a third party `operator`'s rights to send tokens on behalf of `msg.sender`. +Revoke a third party `operator`'s rights to send tokens on behalf of `msg.sender`. -A `RevokedOperator` event MUST be fired on a successful call to this function. +A `RevokedOperator` event MUST be emitted on a successful call to this function. -*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to revoke the token holder (`msg.sender`) as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`). +*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to revoke the token holder (`msg.sender`) as an *operator* for itself (i.e., if `operator` is equal to `msg.sender`). > **parameters** > `operator`: Address to rescind as an *operator* for `msg.sender`. @@ -251,46 +257,50 @@ Indicate whether the `operator` address is an *operator* of the `tokenHolder` ad When an *operator* sends an `amount` of tokens from a *token holder* to a *recipient* with the associated `data` and `operatorData`, the token contract MUST apply the following rules: -- Tokens MAY be sent from any *token holder* to any *recipient*. +- Any *token holder* MAY send tokens to any *recipient*. - The balance of the *token holder* MUST be decreased by the `amount`. - The balance of the *recipient* MUST be increased by the `amount`. -- The balance of the *token holder* MUST be greater or equal to the `amount`—such that its resulting balance is less than zero after the send. -- The token contract MUST fire a `Sent` event with the correct values as defined in the [`Sent` Event][sent]. -- The *token holder* MAY communicate any data in the `data`. -- The *operator* MAY communicate any data in the `operatorData`. -- The contract MAY modify the `data` and `operatorData` -- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* implements the `ERC777TokensRecipient` interface via [ERC820]. -- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* implements the `ERC777TokensRecipient` interface via [ERC820]. -- When calling `tokensToSend` and/or `tokensReceived`: - - `operator` MUST be the address which initiated the send. (Generally the `msg.sender` of the send function call.) - - `from` MUST be the address of the *token holder*. - - `to` MUST be the address of the *recipient*. - - `data` MUST be the data provided by the *token holder* with the OPTIONAL modifications by the token contract. - - `operatorData` MUST be the data provided by the *operator* with the OPTIONAL modifications by the token contract. +- The balance of the *token holder* MUST be greater or equal to the `amount`—such that its resulting balance is greater or equal to zero (`0`) after the send. +- The token contract MUST emit a `Sent` event with the correct values as defined in the [`Sent` Event][sent]. +- The *token holder* MAY communicate any information in the `data`. +- The *operator* MAY communicate any information in the `operatorData`. +- The token contract MUST NOT modify the `data` nor the `operatorData`. +- *Operators* MUST NOT modify the `data` provided by *token holders*. +- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* registers an `ERC777TokensRecipient` implementation via [ERC820]. +- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820]. +- When calling `tokensToSend`, `tokensReceived` or both: + - `operator` MUST be the address which initiated the send. (Generally the `msg.sender` of the send function call.) + - `from` MUST be the address of the *token holder*. + - `to` MUST be the address of the *recipient*. + - `data` MUST be the data provided by the *token holder*. + - `operatorData` MUST be the data provided by the *operator*. The token contract MUST throw when sending in any of the following cases: - - The *operator* address is not an authorized operator for the *token holder* - - The resulting *token holder* and/or *recipient* balances after the send have a granularity smaller than the *granularity* defined by the token contract. - - The address of the *token holder* and/or the *recipient* are `0x0`. +- The *operator* address is not an authorized operator for the *token holder* +- The resulting *token holder* balance or *recipient* balance after the send has a granularity smaller than the *granularity* defined by the token contract. +- The address of the *token holder* or the *recipient* is `0x0`. +- Any of the resulting balance becomes negative, i.e. becomes less than zero (`0`). -The token contract MAY send tokens from many *token holders* and/or to many *recipients*. In this case: +The token contract MAY send tokens from many *token holders* to many *recipients* or both. In this case: - The previous send rules MUST apply to all the *token holders* and all the *recipients* - The sum of all the balances incremented MUST be equal to the total sent `amount`. - The sum of all the balances decremented MUST be equal to the total sent `amount`. -- A `Sent` event MUST be fired for every *token holder* and *recipient* pair with the corresponding amount for each pair. +- A `Sent` event MUST be emitted for every *token holder* and *recipient* pair with the corresponding amount for each pair. - The sum of all the amounts from the `Sent` event MUST be equal to the total sent `amount`. *NOTE*: Mechanisms such as applying a fee on a send is considered as a send to multiple *recipients*: the intended *recipient* and the fee *recipient*. +*NOTE*: Transfer of tokens MAY be chained. For example, if a contract upon receiving tokens sends them further to another address. In this case, the previous send rules apply to each send, in order. + *Implementation Requirement*: - The token contract MUST call the `tokensToSend` hook *before* updating the state. - The token contract MUST call the `tokensReceived` hook *after* updating the state. -I.e. `tokensToSend` MUST be called first, then the balances MUST be updated to reflect the send and finally `tokensReceived` MUST be called *afterward*. This means that a `balanceOf` call within `tokensToSend` returns the balance of the address *before* the send and a `balanceOf` call within `tokensReceived` returns the balance of the address *after* the send. +I.e., `tokensToSend` MUST be called first, then the balances MUST be updated to reflect the send, and finally `tokensReceived` MUST be called *afterward*. Thus a `balanceOf` call within `tokensToSend` returns the balance of the address *before* the send and a `balanceOf` call within `tokensReceived` returns the balance of the address *after* the send. -*NOTE:* `data` and `operatorData` are fields of free bytes provided by the *token holder* and the *operator* respectively. `data` is intended for the recipient. Typically, the recipient SHOULD indicate which data it expects to receive. (Similarly to the data field when sending ether). `operatorData` is intended more for logging purposes and to very specific cases. (Examples include payment references, cheque numbers, counter signatures and more.) In most of the cases the recipient will just ignore the `operatorData` or at most it will log the `operatorData`. +*NOTE*: The `data` and `operatorData` fields are free byte ranges provided by the *token holder* and the *operator* respectively. The `data` field is intended for the recipient. Typically, the recipient SHOULD indicate which data it expects to receive. (Similarly to the data field when sending ether.) The `operatorData` field is intended more for logging purposes and particular cases. (Examples include payment references, cheque numbers, countersignatures and more.) In most of the cases the recipient would ignore the `operatorData`, or at most, it would log the `operatorData`. **`Sent` event**
    @@ -301,18 +311,20 @@ event Sent(address indexed operator, address indexed from, address indexed to, u Indicate a send of `amount` of tokens from the `from` address to the `to` address by the `operator` address. +*NOTE*: This event MUST NOT be emitted outside of a send or an [ERC20] transfer process. + > **parameters** > `operator`: address which triggered the send > `from`: token holder > `to`: token recipient > `amount`: number of tokens to send -> `data`: information attached to the send by the token holder +> `data`: information attached to the send by the token holder (`from`) > `operatorData`: information attached to the send by the `operator` The `send` and `operatorSend` functions described below MUST be implemented to send tokens. Token contracts MAY implement other functions to send tokens. -*NOTE*: An address MAY send an amount of `0`. This is valid and MUST be treated as a regular send. +*NOTE*: An address MAY send an amount of `0`, which is valid and MUST be treated as a regular send. **`send` function** @@ -339,11 +351,11 @@ Send the `amount` of tokens on behalf of the address `from` to the address `to`. The *operator* MUST be `msg.sender`. -*Reminder*: If the *operator* address is not an authorized operator of the `from` address the send must throw. +*Reminder*: If the *operator* address is not an authorized operator of the `from` address the send MUST throw. -*NOTE*: The *operator* MAY pass any data via `operatorData`. The *operator* MUST only pass to `data` data given to it by the *token holder*. The token holder MAY provide this data to the *operator* beforehand through another medium. +*NOTE*: The *operator* MAY pass any information via `operatorData`. The *operator* MUST only pass data given to it by the *token holder* to the `data` parameter. The token holder MAY provide this data to the *operator* beforehand through another medium. -*NOTE*: `from` and `msg.sender` MAY be the same address. I.e an address MAY call `operatorSend` for itself. This call MUST be equivalent to `send` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `send` function). +*NOTE*: `from` and `msg.sender` MAY be the same address. I.e., an address MAY call `operatorSend` for itself. This call MUST be equivalent to `send` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `send` function). > **parameters** > `from`: token holder @@ -354,7 +366,7 @@ The *operator* MUST be `msg.sender`. #### **Minting Tokens** -Minting tokens is the act of producing new tokens. [ERC777] intentionally does not define explicit functions to mint tokens. This intent comes from the wish to not limit the use of the [ERC777] standard as the minting process is generally specific for every token. +Minting tokens is the act of producing new tokens. [ERC777] intentionally does not define specific functions to mint tokens. This intent comes from the wish not to limit the use of the [ERC777] standard as the minting process is generally specific for every token. Nonetheless, the rules below MUST be respected when minting for a *recipient*: @@ -362,30 +374,30 @@ Nonetheless, the rules below MUST be respected when minting for a *recipient*: - The total supply MUST be increased by the amount of tokens minted. - The balance of `0x0` MUST NOT be decreased. - The balance of the *recipient* MUST be increased by the amount of tokens minted. -- The token contract MUST fire a `Minted` event with the correct values as defined in the [`Minted` Event][minted]. -- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* implements the `ERC777TokensRecipient` interface via [ERC820]. +- The token contract MUST emit a `Minted` event with the correct values as defined in the [`Minted` Event][minted]. +- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820]. - When calling `tokensReceived`: - - `operator` MUST be the address which initiated the minting action. (Generally the `msg.sender` of the minting function call.) - - `from` MUST be `0x0`. - - `to` MUST be the address of the *recipient*. - - `data` MUST be empty. - - `operatorData` MUST contain the data with the OPTIONAL modifications by the token contract, provided by the address which initiated the minting action. + - `operator` MUST be the address which initiated the minting action. (Generally the `msg.sender` of the minting function call.) + - `from` MUST be `0x0`. + - `to` MUST be the address of the *recipient*. + - `data` MUST be empty. + - `operatorData` MUST contain the data, provided by the address which initiated the minting action (i.e., the *operator*). The token contract MUST throw when minting in any of the following cases: - The resulting *recipient* balance after the mint has a granularity smaller than the *granularity* defined by the token contract. -- The *recipient* is a contract and it does not implement the `ERC777TokensRecipient` interface via [ERC820]. +- The *recipient* is a contract, and it does not implement the `ERC777TokensRecipient` interface via [ERC820]. - The address of the *recipient* is `0x0`. *[ERC20] compatibility requirement*: -While a `Sent` event MUST NOT be fired when minting, if the token contract is [ERC20] backward compatible, a `Transfer` event with the `from` parameter set to `0x0` MUST be fired as defined in the [ERC20] standard. +While a `Sent` event MUST NOT be emitted when minting, if the token contract is [ERC20] backward compatible, a `Transfer` event with the `from` parameter set to `0x0` SHOULD be emitted as defined in the [ERC20] standard. The token contract MAY mint tokens for multiple *recipients* at once. In this case: - The previous mint rules MUST apply to all the *recipients*. - The sum of all the balances incremented MUST be equal to the total minted amount. -- A `Minted` event MUST be fired for every *recipient* with the corresponding amount for each *recipient*. -- The sum of all the amounts from the `Minted` event MUST be equal to the total sent `amount`. +- A `Minted` event MUST be emitted for every *recipient* with the corresponding amount for each *recipient*. +- The sum of all the amounts from the `Minted` event MUST be equal to the total minted `amount`. **`Minted` event** @@ -395,6 +407,8 @@ event Minted(address indexed operator, address indexed to, uint256 amount, bytes Indicate the minting of `amount` of tokens to the `to` address by the `operator` address. +*NOTE*: This event MUST NOT be emitted outside of a mint process. + > **parameters** > `operator`: address which triggered the mint > `to`: token recipient @@ -403,40 +417,40 @@ Indicate the minting of `amount` of tokens to the `to` address by the `operator` #### **Burning Tokens** -Burning tokens is the act of destroying existing tokens. [ERC777] explicitly defines two functions to burn tokens (`burn` and `operatorBurn`). This makes it easier for wallets and dapps to let *token holders* burn tokens. However, the token contract MAY prevent some or all *token holders* from burning tokens for any reason. The token contract MAY also define other functions to burn tokens. +Burning tokens is the act of destroying existing tokens. [ERC777] explicitly defines two functions to burn tokens (`burn` and `operatorBurn`). These functions facilitate the integration of the burning process in wallets and dapps. However, the token contract MAY prevent some or all *token holders* from burning tokens for any reason. The token contract MAY also define other functions to burn tokens. The rules below MUST be respected when burning the tokens of a *token holder*: - Tokens MAY be burned from any *token holder* address. -- The total supply MUST be decreased by the amount of tokens minted. +- The total supply MUST be decreased by the amount of tokens burned. - The balance of `0x0` MUST NOT be increased. - The balance of the *token holder* MUST be decreased by amount of tokens burned. -- The token contract MUST fire a `Burned` event with the correct values as defined in the [`Burned` Event][burned]. -- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* implements the `ERC777TokensSender` interface via [ERC820]. +- The token contract MUST emit a `Burned` event with the correct values as defined in the [`Burned` Event][burned]. +- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* registers an `ERC777TokensSender` implementation via [ERC820]. - When calling `tokensToSend`: - - `operator` MUST be the address which initiated the minting action. (Generally the `msg.sender` of the minting function call.) - - `from` MUST be the address of the *token holder*. - - `to` MUST be `0x0`. - - `data` MUST be empty. - - `operatorData` MUST contain data with the OPTIONAL modifications by the token contract, provided by the address which initiated the burning action (i.e. the *operator*) and/or the token contract. + - `operator` MUST be the address which initiated the burning action. (Generally the `msg.sender` of the burning function call.) + - `from` MUST be the address of the *token holder*. + - `to` MUST be `0x0`. + - `data` MUST be the data provided by the *token holder*. + - `operatorData` MUST contain data, provided by the address which initiated the burning action (i.e., the *operator*). The token contract MUST throw when burning in any of the following cases: - The address initiating the burn of tokens from a *token holder* MUST be an *operator* of the *token holder*. - The resulting *token holder* balance after the burn has a granularity smaller than the *granularity* defined by the token contract. -- The balance of *token holder* is inferior to the amount of tokens being burnt (i.e. resulting in a negative balance for the *token holder*). +- The balance of *token holder* is inferior to the amount of tokens to burn (i.e., resulting in a negative balance for the *token holder*). - The address of the *token holder* is `0x0`. *[ERC20] compatibility requirement*: -While a `Sent` event MUST NOT be fired when burning, if the token contract is [ERC20] enabled, a `Transfer` event with the `to` parameter set to `0x0` MUST be fired as defined in the [ERC20] standard. +While a `Sent` event MUST NOT be emitted when burning, if the token contract is [ERC20] enabled, a `Transfer` event with the `to` parameter set to `0x0` MUST be emitted as defined in the [ERC20] standard. The token contract MAY burn tokens for multiple *token holders* at once. In this case: - The previous burn rules MUST apply to each *token holders*. - The sum of all the balances decremented MUST be equal to the total burned amount. -- A `Burned` event MUST be fired for every *token holder* with the corresponding amount for each *token holder*. -- The sum of all the amounts from the `Burned` event MUST be equal to the total sent `amount` +- A `Burned` event MUST be emitted for every *token holder* with the corresponding amount for each *token holder*. +- The sum of all the amounts from the `Burned` event MUST be equal to the total burned `amount`. **`Burned` event** @@ -444,11 +458,10 @@ The token contract MAY burn tokens for multiple *token holders* at once. In this event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData) ``` -The `burn` and `operatorBurn` functions described below MUST be implemented to burn tokens. -Token contracts MAY implement other functions to burn tokens. - Indicate the burning of `amount` of tokens from the `from` address by the `operator` address. +*NOTE*: This event MUST NOT be emitted outside of a burn process. + > **parameters** > `operator`: address which triggered the burn > `from`: token holder @@ -456,6 +469,11 @@ Indicate the burning of `amount` of tokens from the `from` address by the `opera > `data`: information attached to the burn by the token holder > `operatorData`: information attached to the burn by the `operator` +The `burn` and `operatorBurn` functions described below MUST be implemented to burn tokens. +Token contracts MAY implement other functions to burn tokens. + +**`burn` function** + ``` solidity function burn(uint256 amount, bytes data) public; ``` @@ -466,8 +484,9 @@ The *operator* and the *token holder* MUST both be the `msg.sender`. > **parameters** > `amount`: number of tokens to burn -> `data`: information attached to the transaction by the token holder +> `data`: information attached to the burn by the token holder +**`operatorBurn` function** ``` solidity function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) public; @@ -477,15 +496,15 @@ Burn the `amount` of tokens on behalf of the address `from`. The *operator* MUST be `msg.sender`. -*Reminder*: If the *operator* address is not an authorized operator of the `from` address the burn must throw. +*Reminder*: If the *operator* address is not an authorized operator of the `from` address the burn MUST throw. -*NOTE*: The *operator* MAY pass any data via `operatorData`. The *operator* MUST only pass to `data` data given to it by the *token holder*. The token holder MAY provide this data to the *operator* beforehand through another medium. +*NOTE*: The *operator* MAY pass any information via `operatorData`. The *operator* MUST only pass to `data` data given to it by the *token holder*. The token holder MAY provide this data to the *operator* beforehand through another medium. -*NOTE*: `from` and `msg.sender` MAY be the same address. I.e an address MAY call `operatorBurn` for itself. This call MUST be equivalent to `burn` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `burn` function). +*NOTE*: `from` and `msg.sender` MAY be the same address. I.e., an address MAY call `operatorBurn` for itself. This call MUST be equivalent to `burn` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `burn` function). -#### **`ERC777TokensSender` And `tokensToSend` Hook** +#### **`ERC777TokensSender` And The `tokensToSend` Hook** -The `tokensToSend` hook notifies of any decrement of balance (send and burn) for a given *token holder*. Any address (regular or contract) wishing to be notified of an debit of their tokens MUST register the address of a contract implementing the `ERC777TokensSender` interface below. A regular address can register a different address—the address of a contract— implementing the interface on its behalf. A contract can register either its address or the address of another contract, but said contract MUST implement the interface. +The `tokensToSend` hook notifies of any decrement of balance (send and burn) for a given *token holder*. Any address (regular or contract) wishing to be notified of token debits from their address MAY register the address of a contract implementing the `ERC777TokensSender` interface described below via [ERC820]. ``` solidity interface ERC777TokensSender { @@ -500,6 +519,8 @@ interface ERC777TokensSender { } ``` +*NOTE*: A regular address MAY register a different address—the address of a contract—implementing the interface on its behalf. A contract MAY register either its address or the address of another contract but said address MUST implement the interface on its behalf. + **`tokensToSend`** ``` solidity @@ -508,41 +529,38 @@ function tokensToSend(address operator, address from, address to, uint value, by Notify a send or burn (if `to` is `0x0`) of `amount` tokens from the `from` address to the `to` address by the `operator` address. +*NOTE*: This function MUST NOT be called outside of a burn, send or [ERC20] transfer process. + > **parameters** > `operator`: address which triggered the balance decrease (through sending or burning) -> `from`: *token holder* (sender) +> `from`: *token holder* > `to`: *token recipient* for a send and `0x` for a burn > `amount`: number of tokens the *token holder* balance is decreased by -> `data`: extra information provided by the *token holder* with the OPTIONAL modifications by the token contract -> `operatorData`: extra information provided by the address which triggered the balance decrease with the OPTIONAL modifications by the token contract +> `data`: extra information provided by the *token holder* +> `operatorData`: extra information provided by the address which triggered the balance decrease The following rules apply to the `tokensToSend` hook: - The `tokensToSend` hook MUST be called every time the balance is decremented. -- The `tokensToSend` hook MUST be called *before* the state is update—i.e. *before* the balance is decremented. +- The `tokensToSend` hook MUST be called *before* the state is updated—i.e. *before* the balance is decremented. - `operator` MUST be the address which triggered the decrease of the balance. -- `from` MUST be the address of the *token holder* whose balance is decreased for a send or burn. -- `from` MUST be `0x0` for a mint. -- `to` MUST be the address of the *recipient* whose balance is increased for a send or mint. +- `from` MUST be the address of the *token holder* whose balance is decreased. +- `to` MUST be the address of the *recipient* whose balance is increased for a send. - `to` MUST be `0x0` for a burn. -- `data` MUST contain the extra information provided by the *token holder* (if any) with the OPTIONAL modifications by the token contract for a send. -- `data` MUST be empty for a mint. -- `operatorData` MUST contain the extra information provided by the address which triggered the decrease of the balance (if any) with the OPTIONAL modifications by the token contract. -- A *token holder* SHALL register its `tokensToSend` hook by registering the address implementing the hook via [ERC820] with the interface name `ERC777TokensSender`. -- A *token holder* CAN register any address (itself or any other) as the implementer of the `ERC777TokensSender` interface. -- A *token holder* MUST register at most one `ERC777TokensSender` implementation at any given time. -- The address claiming to implement the `ERC777TokensSender` interface MUST be a contract which implements the interface. -- The *token holder* MAY block a decrease of its balance by throwing. (I.e. reject the withdrawal of tokens from its account.) -- Any `ERC777TokensSender` implementation MAY be used by more than one *token holder*. +- `data` MUST contain the extra information provided by the *token holder* (if any) for a send or burn. +- `operatorData` MUST contain the extra information provided by the address which triggered the decrease of the balance (if any). +- The *token holder* MAY block a decrease of its balance by throwing. (I.e., reject the withdrawal of tokens from its account.) -*NOTE*: An address can (and MUST) register at most one implementation at a given time. Hence the `ERC777TokensSender` MUST expect to be called by different token contracts. The `msg.sender` of the `tokensToSend` call is expected to be the address of the token contract. +*NOTE*: Multiple *token holders* MAY use the same implementation of `ERC777TokensSender`. + +*NOTE*: An address can register at most one implementation at any given time for all [ERC777] tokens. Hence the `ERC777TokensSender` MUST expect to be called by different token contracts. The `msg.sender` of the `tokensToSend` call is expected to be the address of the token contract. *[ERC20] compatibility requirement*: -This hook takes precedence over [ERC20] and MUST be called (if registered) when calling [ERC20]'s `transfer` and `transferFrom` event. When called from a `transfer`, `operator` MUST be the same value as the `from.` When called from a `transferFrom`, `operator` MUST be the address which issued the `transferFrom` call. +This hook takes precedence over [ERC20] and MUST be called (if registered) when calling [ERC20]'s `transfer` and `transferFrom` event. When called from a `transfer`, `operator` MUST be the same value as the `from`. When called from a `transferFrom`, `operator` MUST be the address which issued the `transferFrom` call. -#### **`ERC777TokensRecipient` And `tokensReceived` Hook** +#### **`ERC777TokensRecipient` And The `tokensReceived` Hook** -The `tokensReceived` hook notifies of any increment of the balance (send and mint) for a given *recipient*. Any address (regular or contract) wishing to be notified of a reception of tokens MUST register the address of a contract implementing the `ERC777TokensRecipient` interface below. A regular address can register a different address—the address of a contract— implementing the interface on its behalf. A contract MUST register either its address or the address of another contract, but said contract MUST implement the interface. +The `tokensReceived` hook notifies of any increment of the balance (send and mint) for a given *recipient*. Any address (regular or contract) wishing to be notified of token debits from their address MAY register the address of a contract implementing the `ERC777TokensSender` interface described below via [ERC820]. ``` solidity interface ERC777TokensRecipient { @@ -557,6 +575,13 @@ interface ERC777TokensRecipient { } ``` +If the *recipient* is a contract, which has not registered an `ERC777TokensRecipient` implementation; the token contract: + +- MUST throw if the `tokensReceived` hook is called from a mint or send call. +- SHOULD accept if the `tokensReceived` hook is called from an ERC20 `transfer` or `transferFrom` call. + +*NOTE*: A regular address MAY register a different address—the address of a contract—implementing the interface on its behalf. A contract MUST register either its address or the address of another contract but said address MUST implement the interface on its behalf. + **`tokensReceived`** ``` solidity @@ -565,41 +590,38 @@ function tokensReceived(address operator, address from, address to, uint amount, Notify a send or mint (if `from` is `0x0`) of `amount` tokens from the `from` address to the `to` address by the `operator` address. +*NOTE*: This function MUST NOT be called outside of a mint, send or [ERC20] transfer process. + > **parameters** > `operator`: address which triggered the balance increase (through sending or minting) > `from`: *token holder* for a send and `0x` for a mint > `to`: *token recipient* > `amount`: number of tokens the *recipient* balance is increased by -> `data`: extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint, with the OPTIONAL modifications by the token contract -> `operatorData`: extra information provided by the address which triggered the balance increase with the OPTIONAL modifications by the token contract +> `data`: extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint, +> `operatorData`: extra information provided by the address which triggered the balance increase + +The following rules apply to the `tokensToSend` hook: - The `tokensReceived` hook MUST be called every time the balance is incremented. - The `tokensReceived` hook MUST be called *after* the state is update—i.e. *after* the balance is incremented. - `operator` MUST be the address which triggered the increase of the balance. -- `from` MUST be the address of the *token holder* whose balance is decreased for a send or burn. +- `from` MUST be the address of the *token holder* whose balance is decreased for a send. - `from` MUST be `0x0` for a mint. -- `to` MUST be the address of the *recipient* whose balance is increased for a send or mint. -- `to` MUST be `0x0` for a burn. -- `data` MUST contain the extra information provided by the *token holder* (if any) with the OPTIONAL modifications by the token contract for a send. +- `to` MUST be the address of the *recipient* whose balance is increased. +- `data` MUST contain the extra information provided by the *token holder* (if any) for a send. - `data` MUST be empty for a mint. -- `operatorData` MUST contain the extra information provided by the address which triggered the increase of the balance (if any) with the OPTIONAL modifications by the token contract. -- A *token holder* SHALL register its `tokensReceived` hook by registering the address implementing the hook via [ERC820] with the interface name `ERC777TokensRecipient`. -- If the *recipient* is a contract, which has not registered an `ERC777TokensRecipient` implementation; the token contract: - - MUST throw if the `tokensReceived` hook is called from a mint or send call. - - SHOULD accept if the `tokensReceived` hook is called from an ERC20 `transfer` or `transferFrom` call. -- A *token holder* CAN register any address (itself or any other) as the implementer of the `ERC777TokensRecipient` interface. -- A *token holder* MUST register at most one `ERC777TokensRecipient` implementation at any given time. -- The address claiming to implement the `ERC777TokensRecipient` interface MUST be a contract which implements the interface. -- The *token holder* MAY block an increase of its balance by throwing. (I.e. reject the reception of tokens.) -- Any `ERC777TokensRecipient` implementation MAY be used by more than one *token holder*. +- `operatorData` MUST contain the extra information provided by the address which triggered the increase of the balance (if any). +- The *token holder* MAY block an increase of its balance by throwing. (I.e., reject the reception of tokens.) -*NOTE*: An address can (and MUST) register at most one implementation at a given time. Hence the `ERC777TokensRecipient` MUST expect to be called by different token contracts. The `msg.sender` of the `tokensReceived` call is expected to be the address of the token contract. +*NOTE*: Multiple *token holders* MAY use the same implementation of `ERC777TokensRecipient`. + +*NOTE*: An address can register at most one implementation at any given time for all [ERC777] tokens. Hence the `ERC777TokensRecipient` MUST expect to be called by different token contracts. The `msg.sender` of the `tokensReceived` call is expected to be the address of the token contract. *[ERC20] compatibility requirement*: -This hook takes precedence over [ERC20] and MUST be called (if registered) when calling [ERC20]'s `transfer` and `transferFrom` event. When called from a `transfer`, `operator` MUST be the same value as the `from.` When called from a `transferFrom`, `operator` MUST be the address which issued the `transferFrom` call. +This hook takes precedence over [ERC20] and MUST be called (if registered) when calling [ERC20]'s `transfer` and `transferFrom` event. When called from a `transfer`, `operator` MUST be the same value as the `from`. When called from a `transferFrom`, `operator` MUST be the address which issued the `transferFrom` call. #### **Note On Gas Consumption** -Dapps and wallets SHOULD first estimate the gas required when sending, minting,or burning tokens—using [`eth_estimateGas`][eth_estimateGas]—to avoid running out of gas during the transaction. +Dapps and wallets SHOULD first estimate the gas required when sending, minting, or burning tokens—using [`eth_estimateGas`][eth_estimateGas]—to avoid running out of gas during the transaction. ### Logo @@ -610,17 +632,17 @@ Dapps and wallets SHOULD first estimate the gas required when sending, minting,o The logo MAY be used, modified and adapted to promote valid [ERC777] token implementations and [ERC777] compliant technologies such as wallets and dapps. -[ERC777] token contract authors CAN create a specific logo for their token based on this logo. +[ERC777] token contract authors MAY create a specific logo for their token based on this logo. The logo MUST NOT be used to advertise, promote or associate in any way technology—such as tokens—which is not [ERC777] compliant. -The logo for the standard can be found in the [`/assets/eip-777/logo`][logos] folder in `svg` and `png` formats. The `png` version of the logo offers a few sizes in pixels. If needed, other sizes CAN be created by converting from `svg` into `png`. +The logo for the standard can be found in the [`/assets/eip-777/logo`][logos] folder in `SVG` and `PNG` formats. The `PNG` version of the logo offers a few sizes in pixels. If needed, other sizes MAY be created by converting from `SVG` into `PNG`. ## Rationale -This standard solves some of the shortcomings of [ERC20]. It avoids the problems of the [EIP223]. It is backward compatible with [ERC20]. Lastly and goes a step further by allowing *operators* (generally contracts) which can manage the tokens in the same way that the [ERC20] with infinite `approve` was allowed and hooks to give greater control to *token holders* over their tokens. +This standard solves some of the shortcomings of [ERC20] while maintaining backward compatibility with [ERC20]. It avoids the problems and vulnerabilities of he [EIP223]. -Also, the usage of [ERC820] allows backward compatibility with wallets and existing contracts without having to be redeployed thanks proxy contracts implementing the hooks. +It goes a step further by allowing *operators* (generally contracts) which can manage the tokens in the same way that the [ERC20] with infinite `approve` was allowed. Finally, it adds hooks to provide further control to *token holders* over their tokens. Note that, the usage of [ERC820] provides backward compatibility with wallets and existing contracts without having to be redeployed thanks proxy contracts implementing the hooks. ## Backward Compatibility @@ -630,16 +652,16 @@ This EIP does not use `transfer` and `transferFrom` and uses `send` and `operato This standard allows the implementation of [ERC20] functions `transfer`, `transferFrom`, `approve` and `allowance` alongside to make a token fully compatible with [ERC20]. -The token CAN implement `decimals()` for backward compatibility with [ERC20]. If implemented, it MUST always return `18`. +The token MAY implement `decimals()` for backward compatibility with [ERC20]. If implemented, it MUST always return `18`. -Therefore a token contract CAN implement both [ERC20] and [ERC777] in parallel. The specification of the `view` functions (such as `name`, `symbol`, `balanceOf`, `totalSupply`) and internal data (such as the mapping of balances) overlap without problems. Note however that the following functions are mandatory in [ERC777] and MUST be implemented: `name`, `symbol` `balanceOf` and `totalSupply` (`decimal` is not part of the [ERC777] standard). +Therefore a token contract MAY implement both [ERC20] and [ERC777] in parallel. The specification of the `view` functions (such as `name`, `symbol`, `balanceOf`, `totalSupply`) and internal data (such as the mapping of balances) overlap without problems. Note however that the following functions are mandatory in [ERC777] and MUST be implemented: `name`, `symbol` `balanceOf` and `totalSupply` (`decimal` is not part of the [ERC777] standard). The state-modifying functions from both standards are decoupled and can operate independently from each other. Note that [ERC20] functions SHOULD be limited to only being called from old contracts. If the token implements [ERC20], it MUST register the `ERC20Token` interface with its own address via [ERC820]. If the contract has a switch to enable or disable [ERC20] functions, every time the switch is triggered, the token MUST register or unregister its own address accordingly the `ERC20Token` interface via [ERC820]. (Unregistering implies setting the address to `0x0`.) The difference for new contracts implementing [ERC20] is that -`tokensToSend` and `tokensReceived` hooks take precedence over [ERC20]. This means that even with an [ERC20] `transfer` and `transferFrom` call, the token contract MUST check via [ERC820] if the `from` and the `to` address implement `tokensToSend` and `tokensReceived` hook respectively. If any hook is implemented, it MUST be called. Note that when calling [ERC20] `transfer` on a contract, if the contract does not implement `tokensReceived`, the `transfer` call SHOULD still be accepted even if this means the tokens will probably be locked. +`tokensToSend` and `tokensReceived` hooks take precedence over [ERC20]. Even with an [ERC20] `transfer` and `transferFrom` call, the token contract MUST check via [ERC820] if the `from` and the `to` address implement `tokensToSend` and `tokensReceived` hook respectively. If any hook is implemented, it MUST be called. Note that when calling [ERC20] `transfer` on a contract, if the contract does not implement `tokensReceived`, the `transfer` call SHOULD still be accepted even if this means the tokens will probably be locked. The table below summarizes the different actions the token contract MUST take when sending, minting and transferring token via [ERC777] and [ERC20]: @@ -677,11 +699,11 @@ The table below summarizes the different actions the token contract MUST take wh -There is no particular action to take if `tokensToSend` is not implemented. The transfer MUST proceed and be canceled only if another condition is not respected such as lack of funds or a throw in `tokensReceived` (if present). +There is no particular action to take if `tokensToSend` is not implemented. The transfer MUST proceed and only be canceled if another condition is not respected such as lack of funds or a throw in `tokensReceived` (if present). -During a send, mint and burn, the respective `Sent`, `Minted` and `Burned` events MUST be fired. In addition, if the token contract declares that it implements `ERC20Token` via [ERC820], the token contract MUST fire a `Transfer` event (as specified in the [ERC20] standard). During an [ERC20]'s `transfer` or `transferFrom` functions, a valid `Sent` event MUST be fired. +During a send, mint and burn, the respective `Sent`, `Minted` and `Burned` events MUST be emitted. Furthermore, if the token contract declares that it implements `ERC20Token` via [ERC820], the token contract SHOULD emit a `Transfer` event for minting and MUST emit a `Transfer` event for sending and burning (as specified in the [ERC20] standard). During an [ERC20]'s `transfer` or `transferFrom` functions, a valid `Sent` event MUST be emitted. -This means that for any movement of tokens, two events MAY be fired: an [ERC20] `Transfer` and an [ERC777] `Sent`, `Minted` or `Burnt` (depending on the type of movement). Third-party developers MUST be careful not to consider both events as separate movements. As a general rule, if the token is considered by an application as an ERC20 token, then only the `Transfer` event MUST be considered. If the token is considered by an application as an ERC777 token, the only the `Sent`, `Minted` and `Burnt` events MUST be considered. +Hence for any movement of tokens, two events MAY be emitted: an [ERC20] `Transfer` and an [ERC777] `Sent`, `Minted` or `Burned` (depending on the type of movement). Third-party developers MUST be careful not to consider both events as separate movements. As a general rule, if an application considers the token as an ERC20 token, then only the `Transfer` event MUST be taken into account. If the application considers the token as an ERC777 token, then only the `Sent`, `Minted` and `Burned` events MUST be considered. ## Test Cases @@ -689,7 +711,7 @@ The [repository with the reference implementation][jacquesd/ERC777] contains all ## Implementation -The repository at [jacquesd/ERC777] contains the [reference implementation]. +The GitHub repository [jacquesd/ERC777] contains the [reference implementation]. ## Copyright From 984ddd0589dd6bf2323335c5f5eb1b71aa566df4 Mon Sep 17 00:00:00 2001 From: EOS Classic Date: Tue, 31 Jul 2018 20:26:07 +0900 Subject: [PATCH 077/177] EIP-1276: Eliminate Difficulty Bomb and Adjust Block Reward on Constantinople Shift (#1276) --- EIPS/eip-1276.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 EIPS/eip-1276.md diff --git a/EIPS/eip-1276.md b/EIPS/eip-1276.md new file mode 100644 index 00000000..2225ad3d --- /dev/null +++ b/EIPS/eip-1276.md @@ -0,0 +1,63 @@ +--- +eip: 1276 +title: Eliminate Difficulty Bomb and Adjust Block Reward on Constantinople Shift +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 +created: 2018-07-31 +--- + +## Simple Summary +The average block times are increasing due to the factor of difficulty logic well known as difficulty bomb. This EIP proposes to eliminate the difficulty bomb forever and to reduce the block rewards with the Constantinople fork, the second part of the Metropolis fork. + +## Abstract +Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty without considering the current block number. Furthermore, block rewards will be adjusted to a base of 2 ETH, uncle and nephew rewards will be adjusted accordingly. + +## Motivation +Block time has been played a most important role on blockchain ecosystem, and it is being adjusted by the logic of mining difficulty calculation that is already implemented on the node client as a part of proof-of-work consensus. Last year, average block time rapidly increased due to the wrong design of difficulty logic that is meant to be changed on the part of Casper upgrade, however, implementation of casper has been delayed therefore it was inevitable to delay the difficulty bomb in order to prevent the significant delay of processing transactions on ethereum network. + +Despite of the successful hardfork to delay the activation of difficulty bomb, activation of the difficulty bomb is expected to happen again on the upcoming period before implementing casper protocol on ethereum network. Therefore, completely removing the difficulty bomb is the most proper way to response the block time increase instead of delaying it again. + +Also decreasing the block mining reward along with difficulty bomb removal is expected to help the growth of the stable ethereum ecosystem, right now ethereum dominates 92% of the total hashrate share of ethash based chains, and this corresponds to a tremendous level of energy consumption. As this energy consumption has a correlated environmental cost the network participants have an ethical obligation to ensure this cost is not higher than necessary. At this time, the most direct way to reduce this cost is to lower the block reward in order to limit the appeal of ETH mining. Unchecked growth in hashrate is also counterproductive from a security standpoint. Reducing the reward also decreases the likelihood of a miner driven chain split as Ethereum approaches proof-of-stake. + +## Specification +#### Remove Exponential Component of Difficulty Adjustment +For the purposes of `calc_difficulty`, simply remove the exponential difficulty adjustment component, `epsilon`, i.e. the `int(2**((block.number // 100000) - 2))`. + +#### Adjust Block, Uncle, and Nephew rewards +To ensure a constant Ether issuance, adjust the block reward to `new_block_reward`, where + + new_block_reward = 2_000_000_000_000_000_000 if block.number >= CNSTNTNPL_FORK_BLKNUM else block.reward + +(2E18 wei, or 2,000,000,000,000,000,000 wei, or 2 ETH). + +Analogue, if an uncle is included in a block for `block.number >= CNSTNTNPL_FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (8 - k) * new_block_reward / 8 + +This is the existing pre-Constantinople formula for uncle rewards, simply adjusted with `new_block_reward`. + +The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is + + new_nephew_reward = new_block_reward / 32 + +This is the existing pre-Constantinople formula for nephew rewards, simply adjusted with `new_block_reward`. + +## Rationale +This will completely remove the difficulty bomb on difficulty adjustment algorithm without delaying the difficulty bomb again, therefore it is possible to prevent network delay on the beginning of 2019. + +This EIP-1276 opposes directly the intent of [EIP-1234](https://github.com/ethereum/EIPs/issues/1234) which should be also considered in discussions. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, _Constantinople_. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementation +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/). From 9ae526228cf2e00f7d044194c735e853224fc1f5 Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Tue, 31 Jul 2018 06:35:43 -0700 Subject: [PATCH 078/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index e1448797..19820adc 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -49,27 +49,25 @@ The [ERC165 standard](https://eips.ethereum.org/EIPS/eip-165) must be applied to Authenticating whether a user or contract can execute some action works the same for both top-down and bottom-up composables. -A `rootOwner` refers to the owner address at the top of a tree of composables and ERC721 tokens. An `immediateOwner` refers to an address that directly owns a composable or ERC721 token with no composables between it and the token it owns. +A `rootOwner` refers to the owner address at the top of a tree of composables and ERC721 tokens. -Authentication within any composable is done by finding the rootOwner and comparing it to `msg.sender`, the return result of `getApproved(tokenId)` and the return result of `isApprovedForAll(rootOwner, msg.sender)`. If no match is found then the immediateOwner of the composable is found and compared to `msg.sender` and the return result of `isApprovedForAll(immediateOwner, msg.sender)`. If a match is found then authentication passes, otherwise authentication fails and the contract throws. +Authentication within any composable is done by finding the rootOwner and comparing it to `msg.sender`, the return result of `getApproved(tokenId)` and the return result of `isApprovedForAll(rootOwner, msg.sender)`. If a match is found then authentication passes, otherwise authentication fails and the contract throws. Here is an example of authentication code: ```solidity -require(rootOwner == msg.sender || isApprovedForAll(rootOwner,msg.sender) || - getApproved(tokenId) == msg.sender || immediateOwner == msg.sender || - isApprovedForAll(immediateOwner,msg.sender); +address rootOwner = address(rootOwnerOf(_tokenId)); +require(rootOwner == msg.sender || + isApprovedForAll(rootOwner,msg.sender) || + getApproved(tokenId) == msg.sender; ``` -The `approve(address _approved, uint256 _tokenId)` and `getApproved(uint256 _tokenId)` ERC721 functions are implemented specifically for the rootOwner, not the immediateOwner. This enables a tree of composables to be transferred to a new rootOwner without worrying about which addresses have been approved in child composables, because any prior approves can only be used by the prior rootOwner. +The `approve(address _approved, uint256 _tokenId)` and `getApproved(uint256 _tokenId)` ERC721 functions are implemented specifically for the rootOwner. This enables a tree of composables to be transferred to a new rootOwner without worrying about which addresses have been approved in child composables, because any prior approves can only be used by the prior rootOwner. Here are example implementations: ```solidity function approve(address _approved, uint256 _tokenId) external { - address immediateOwner = tokenIdToTokenOwner[_tokenId]; - require(immediateOwner != address(0)); address rootOwner = address(rootOwnerOf(_tokenId)); - require(rootOwner == msg.sender || isApprovedForAll(rootOwner,msg.sender) || - immediateOwner == msg.sender || isApprovedForAll(immediateOwner,msg.sender)); + require(rootOwner == msg.sender || isApprovedForAll(rootOwner,msg.sender)); rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] = _approved; emit Approval(rootOwner, _approved, _tokenId); From fc6d56f495a0bbda6764f56ab0cfa594335a1b6e Mon Sep 17 00:00:00 2001 From: Chaitanya Potti Date: Tue, 31 Jul 2018 19:06:13 +0530 Subject: [PATCH 079/177] Create eip-1261 (#1268) * Create eip-1261 * updated eip 1261 * edited title to remove erc number --- EIPS/eip-1261.md | 247 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 EIPS/eip-1261.md diff --git a/EIPS/eip-1261.md b/EIPS/eip-1261.md new file mode 100644 index 00000000..d8774755 --- /dev/null +++ b/EIPS/eip-1261.md @@ -0,0 +1,247 @@ +--- +eip: 1261 +title: Membership Verification Token (MVT) +author: Chaitanya Potti , Partha Bhattacharya +type: Standards Track +category: ERC +status: Draft +created: 2018-07-14 +discussions-to: https://github.com/ethereum/EIPs/issues/1261 + +--- + +## Simple Summary + +A standard interface for Membership Verification Token(MVT). + +## Abstract + +The following standard allows for the implementation of a standard API for Membership Verification Token within smart contracts(called entities). This standard provides basic functionality to track membership of individuals in certain on-chain ‘organizations’. This allows for several use cases like automated compliance, and several forms of governance and membership structures. + +We considered use cases of MVTs being assigned to individuals which are non-transferable and revocable by the owner. MVTs can represent proof of recognition, proof of membership, proof of right-to-vote and several such otherwise abstract concepts on the blockchain. The following are some examples of those use cases, and it is possible to come up with several others: + +- Voting: Voting is inherently supposed to be a permissioned activity. So far, onchain voting systems are only able to carry out voting with coin balance based polls. This can now change and take various shapes and forms. +- Passport issuance, social benefit distribution, Travel permit issuance, Drivers licence issuance are all applications which can be abstracted into membership, that is belonging of an individual to a small set, recognized by some authority as having certain entitlements, without needing any individual specific information(right to welfare, freedom of movement, authorization to operate vehicles, immigration) +- Investor permissioning: Making regulatory compliance a simple on chain process. Tokenization of securities, that are streamlined to flow only to accredited addresses, tracing and certifying on chain addresses for AML purposes. +- Software licencing: Software companies like game developers can use the protocol to authorize certain hardware units(consoles) to download and use specific software(games) + + +In general, an individual can have different memberships in his day to day life. The protocol allows for the creation of software that puts everything all at one place. His identity can be verified instantly. Imagine a world where you don't need to carry a wallet full of identity cards (Passport, gym membership, SSN, Company ID etc) and organizations can easily keep track of all its members. Organizations can easily identify and disallow fake identities. + +## Motivation + +A standard interface allows any user,applications to work with any MVT on Ethereum. We provide for simple ERC-1261 smart contracts. Additional applications are discussed below. + +This standard is inspired from the fact that voting on the blockchain is done with token balance weights. This has been greatly detrimental to the formation of flexible governance systems on the blockchain, despite the tremendous governance potential that blockchains offer. The idea was to create a permissioning system that allows organizations to vet people once into the organization on the blockchain, and then gain immense flexibility in the kind of governance that can be carried out. + +**Trade-off between trustlessness and usability:** +To truly understand the value of the protocol, it is important to understand the trade-off we are treading on. The MVT contract allows the creator to revoke the token, and essentially confiscate the membership of the member in question. To some, this might seem like an unacceptable flaw, however this is a design choice, and not a flaw. +The choice may seem to place a great amount of trust in the individuals who are managing the entity contract(entity owners). If the interests of the entity owner conflict with the interests of the members, the owner may resort to addition of fake addresses(to dominate consensus) or evicting members(to censor unfavourable decisions). At first glance this appears to be a major shortcomings, because the blockchain space is used to absolute removal of authority in most cases. Even the official definition of a dapp requires the absence of any party that manages the services provided by the application. However, the trust in entity owners is not misplaced, if it can be ensured that the interests of entity owners are aligned with the interests of members. +Another criticism of such a system would be that the standard edge of blockchain intermediation - “you cannot bribe the system if you don’t know who to bribe” - no longer holds. It is possible to bribe an entity owner into submission, and get them to censor or fake votes. There are several ways to respond to this argument. First of all, all activities, such as addition of members, and removal of members can be tracked on the blockchain and traces of such activity cannot be removed. It is not difficult to build analytics tools to detect malicious activity(adding 100 fake members suddenly who vote in the direction/sudden removal of a number of members voting in a certain direction). Secondly, the entity owners’ power is limited to the addition and removal of members. This means that they cannot tamper any votes. They can only alter the counting system to include fake voters or remove real voters. Any sensible auditor can identify the malicious/victim addresses and create an open source audit tool to find out the correct results. The biggest loser in this attack will be the entity owner, who has a reputation to lose. +Finally, one must understand why we are taking a step away from trustlessness in this trade-off. The answer is usability. Introducing a permissioning system expands the scope of products and services that can be delivered through the blockchain, while leveraging other aspects of the blockchain(cheap, immutable, no red-tape, secure). Consider the example of the driver licence issuing agency using the ERC-1300 standard. This is a service that simply cannot be deployed in a completely trustless environment. The introduction of permissioned systems expanded the scope of services on the blockchain to cover this particular service. Sure, they have the power to revoke a person’s licence for no reason. But will they? Who stands to lose the most, if the agency acts erratically? The agency itself. Now consider the alternative, the way licences(not necessarily only drivers licence, but say shareholder certificates and so on) are issued, the amount of time consumed, the complete lack of transparency. One could argue that if the legacy systems providing these services really wanted to carry out corruption and nepotism in the execution of these services, the present systems make it much easier to do so. Also, they are not transparent, meaning that there is no way to even detect if they act maliciously. +All that being said, we are very excited to share our proposal with the community and open up to suggestions in this space. + + +## 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 ERC-1261 compliant contract must implement the `ERC1261` interface: + +```solidity +pragma solidity ^0.4.24; + +/// @title ERC-1261 MVT Standard +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1261.md +interface ERC1261 { + /// @dev This emits when a token is assigned to a member. + event Assigned(address _to); + + /// @dev This emits when a membership is revoked + event Revoked(address _to); + + /// @dev MVT's 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 membership + /// @return whether the member owns the token + function isCurrentMember(address _to) external view returns (bool); + + /// @notice Find the member of an MVT + /// @dev index starts from zero. Useful to query all addresses (past or present) + /// @param _tokenId The index + /// @return The address of the owner of the IVM contract + function getAddressAtIndex(uint256 _index) external view returns (address); + + /// @notice Assigns membership of an MVT from owner address to another address + /// @dev Throws if the member already has the token. + /// Throws if `_to` is the zero address. + /// Throws if the `msg.sender` is not an owner. + /// The entity assigns the membership to each individual. + /// When transfer is complete, this function emits the Assigned event. + /// @param _to The member + function assign(address _to) external; + + /// @notice Requests membership from any address + /// @dev Throws if the `msg.sender` already has the token. + /// the individual `msg.sender` can request for a membership and if some exisiting criteria are satisfied, + /// the individual `msg.sender` receives the token. + /// When transfer is complete, this function emits the Assigned event. + function assignTo() external payable; + + /// @notice Revokes the membership + /// @dev This removes the membership of the user. + /// Throws if the `_from` is not an owner of the token. + /// Throws if the `msg.sender` is not an owner. + /// Throws if `_from` is the zero address. + /// When transaction is complete, this function emits the Revoked event. + /// @param _from The current owner of the MVT + function revoke(address _from) external; + + /// @notice Revokes membership from any address + /// @dev Throws if the `msg.sender` already doesn't have the token. + /// the individual `msg.sender` can revoke his/her membership. + /// When transfer is complete, this function emits the Revoked event. + function revokeFrom() external payable; +} + +``` + +The **metadata extension** is OPTIONAL for ERC-1261 smart contracts (see "caveats", below). This allows your smart contract to be interrogated for its name and for details about the organization which your IVM tokens represent. + +```solidity +/// @title ERC-1261 IVM Token Standard, optional metadata extension +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1261.md +interface ERC1261Metadata /* is ERC1261 */ { + /// @notice A descriptive name for a collection of MVTs in this contract + function name() external view returns (string _name); + + /// @notice An abbreviated name for MVTs in this contract + function symbol() external view returns (string _symbol); +} +``` + +This is the "ERC1261 Metadata JSON Schema" referenced above. + +```json +{ + "title": "Organization Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the organization to which this MVT represents", + }, + "description": { + "type": "string", + "description": "Describes the organization to which this MVT represents", + } + } +} +``` + +### Caveats + +The 0.4.24 Solidity interface grammar is not expressive enough to document the ERC-1261 standard. A contract which complies with ERC-1261 MUST also abide by the following: + +- Solidity issue #3412: The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation MUST meet the mutability guarantee in this interface and you MAY meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.24 is that you can edit this interface to add stricter mutability before inheriting from your contract. +- Solidity issue #3419: A contract that implements `ERC1261Metadata` SHALL also implement `ERC1261`. +- Solidity issue #2330: If a function is shown in this specification as `external` then a contract will be compliant if it uses `public` visibility. As a workaround for version 0.4.24, you can edit this interface to switch to `public` before inheriting from your contract. +- Solidity issues #3494, #3544: Use of `this.*.selector` is marked as a warning by Solidity, a future version of Solidity will not mark this as an error. + +*If a newer version of Solidity allows the caveats to be expressed in code, then this EIP MAY be updated and the caveats removed, such will be equivalent to the original specification.* + +## Rationale + +There are many potential uses of Ethereum smart contracts that depend on tracking membership. Examples of existing or planned MVT systems are Vault, a DAICO platform, and Stream, a security token framework. Future uses include the implementation of direct democracy, in-game memberships and badges, licence and travel document issuance, electronic voting machine trails, software licencing and many more. + +**MVT Word Choice:** + +Since the tokens are non transferable and revocable, they function like membership cards. Hence the word membership verification token. + +**Transfer Mechanism** + +MVTs can't be transferred. This is a design choice, and one of the features that distinguishes this protocol. + +**Assign and Revoke mechanism** + +The assign and revoke functions' documentation only specify conditions when the transaction MUST throw. Your implementation MAY also throw in other situations. This allows implementations to achieve interesting results: + +- **Disallow additional memberships after a condition is met** — Sample contract found on Electus Protocol +- **Blacklist certain address from receiving IVM tokens** — Sample contract found on Electus Protocol +- **Disallow additional memberships after a certain time is reached** — Sample contract found on Electus Protocol +- **Charge a fee to user of a transaction** — require payment when calling `assign` and `revoke` so that condition checks from external sources can be made + +**Gas and Complexity** (regarding the enumeration extension) + +This specification contemplates implementations that manage a few and *arbitrarily large* numbers of MVTs. If your application is able to grow then avoid using for/while loops in your code. These indicate your contract may be unable to scale and gas costs will rise over time without bound + +**Privacy** + +Personal information: The protocol does not put any personal information on to the blockchain, so there is no compromise of privacy in that respect. +Membership privacy: The protocol by design, makes it public which addresses are/aren’t members. Without making that information public, it would not be possible to independently audit governance activity or track admin(entity owner) activity. + +**Metadata Choices** (metadata extension) + +We have required `name` and `symbol` functions in the metadata extension. Every token EIP and draft we reviewed (ERC-20, ERC-223, ERC-677, ERC-777, ERC-827) included these functions. + +We remind implementation authors that the empty string is a valid response to `name` and `symbol` if you protest to the usage of this mechanism. We also remind everyone that any smart contract can use the same name and symbol as *your* contract. How a client may determine which ERC-1261 smart contracts are well-known (canonical) is outside the scope of this standard. + +A mechanism is provided to associate MVTs with URIs. We expect that many implementations will take advantage of this to provide metadata for each MVT system. The URI MAY be mutable (i.e. it changes from time to time). We considered an MVT representing membership of a place, in this case metadata about the organization can naturally change. + +Metadata is returned as a string value. Currently this is only usable as calling from `web3`, not from other contracts. This is acceptable because we have not considered a use case where an on-blockchain application would query such information. + +*Alternatives considered: put all metadata for each asset on the blockchain (too expensive), use URL templates to query metadata parts (URL templates do not work with all URL schemes, especially P2P URLs), multiaddr network address (not mature enough)* + +**Community Consensus** + +We have been very inclusive in this process and invite anyone with questions or contributions into our discussion. However, this standard is written only to support the identified use cases which are listed herein. + +## Backwards Compatibility + +We have adopted `name` and `symbol` semantics from the ERC-20 specification. + +Example MVT implementations as of July 2018: + +- Electus Protocol(https://github.com/chaitanyapotti/ElectusProtocol/blob/master/Vault/VaultToken.sol) + +## Test Cases + +Electus Protocol ERC-1261 Token includes test cases written using Truffle. + +## Implementations + +Electus Protocol ERC1261 -- a reference implementation + +- MIT licensed, so you can freely use it for your projects +- Includes test cases + +## References + +**Standards** + +1. ERC-20 Token Standard. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md +1. ERC-173 Owned Standard. https://github.com/ethereum/EIPs/issues/173 +1. Ethereum Name Service (ENS). https://ens.domains +1. JSON Schema. http://json-schema.org/ +1. Multiaddr. https://github.com/multiformats/multiaddr +1. RFC 2119 Key words for use in RFCs to Indicate Requirement Levels. https://www.ietf.org/rfc/rfc2119.txt + +**Issues** + +1. The Original ERC-1261 Issue. https://github.com/ethereum/eips/issues/1261 +1. Solidity Issue \#2330 -- Interface Functions are Axternal. https://github.com/ethereum/solidity/issues/2330 +1. Solidity Issue \#3412 -- Implement Interface: Allow Stricter Mutability. https://github.com/ethereum/solidity/issues/3412 +1. Solidity Issue \#3419 -- Interfaces Can't Inherit. https://github.com/ethereum/solidity/issues/3419 + +**Discussions** + +1. Gitter #EIPs (announcement of first live discussion). https://gitter.im/ethereum/EIPs?at=5b5a1733d2f0934551d37642 +1. ERC-1261 (announcement of first live discussion). https://github.com/ethereum/eips/issues/1261 + +**MVT Implementations and Other Projects** + +1. Electus Protocol ERC-1261 Token. https://github.com/chaitanyapotti/ElectusProtocol + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From b41669ede219b9599a6a1c637e578b8a444ad6b1 Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Tue, 31 Jul 2018 17:35:49 +0200 Subject: [PATCH 080/177] Automatically merged updates to draft EIP(s) 777 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-777.md | 303 +++++++++++++++++++++++++----------------------- 1 file changed, 161 insertions(+), 142 deletions(-) diff --git a/EIPS/eip-777.md b/EIPS/eip-777.md index e2177f77..68562861 100644 --- a/EIPS/eip-777.md +++ b/EIPS/eip-777.md @@ -1,7 +1,7 @@ --- eip: 777 title: A New Advanced Token Standard -author: Jordi Baylina , Jacques Dafflon , Thomas Shababi +author: Jacques Dafflon , Jordi Baylina , Thomas Shababi discussions-to: https://github.com/ethereum/EIPs/issues/777 status: Draft type: Standards Track @@ -14,8 +14,6 @@ requires: 820 This EIP defines standard interfaces and behaviors for token contracts. -*The repository containing the reference implementation for this standard can be found at [github.com/jacquesd/ERC777][jacquesd/ERC777] and installed via npm with `npm install erc777`.* - ## Abstract This standard defines a new way to interact with a token contract while remaining backward compatible with [ERC20]. @@ -29,11 +27,11 @@ It takes advantage of [ERC820] to find out whether and where to notify contracts This standard tries to improve the widely used [ERC20] token standard. The main advantages of this standard are: 1. Uses the same philosophy as Ether in that tokens are sent with `send(dest, value, data)`. -2. Both contracts and regular addresses can control and reject which token they send by registering a `tokensToSend` hook. (Rejection is done by throwing in the hook function.) -3. Both contracts and regular addresses can control and reject which token they receive by registering a `tokensReceived` hook. (Rejection is done by throwing in the hook function.) -4. The `tokensReceived` hook also avoids the double call needed in the [ERC20] standard (`approve`/`transferFrom`) to send tokens to a contract. +2. Both contracts and regular addresses can control and reject which token they send by registering a `tokensToSend` hook. (Rejection is done by `revert`ing in the hook function.) +3. Both contracts and regular addresses can control and reject which token they receive by registering a `tokensReceived` hook. (Rejection is done by `revert`ing in the hook function.) +4. The `tokensReceived` hook allows to send tokens to a contract and notify it in a single transaction, unlike [ERC20] which require a double call (`approve`/`transferFrom`) to achieve this. 5. The token holder can "authorize" and "revoke" operators which can send tokens on their behalf. These operators are intended to be verified contracts such as an exchange, a cheque processor or an automatic charging system. -6. Every token transaction contains a `data` bytes field and a similar `operatorData` to be used freely by the token holder and the operator respectively to pass data to the recipient. +6. Every token transaction contains a `data` bytes field and a similar `operatorData` to be used freely to pass data to the recipient. 7. It is backward compatible with wallets that do not contain the `tokensReceived` hook function by deploying a proxy contract implementing the `tokensReceived` hook for the wallet. ## Specification @@ -67,8 +65,8 @@ interface ERC777Token { bytes data, bytes operatorData ); - event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData); - event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); + event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); + event Burned(address indexed operator, address indexed from, uint256 amount, bytes operatorData); event AuthorizedOperator(address indexed operator, address indexed tokenHolder); event RevokedOperator(address indexed operator, address indexed tokenHolder); } @@ -91,7 +89,7 @@ The `view` functions detailed below MUST be implemented. Returns the name of the token, e.g., `"MyToken"`. -> **returns:** Name of the token +> **returns:** Name of the token. **`symbol` function** @@ -101,7 +99,7 @@ function symbol() public view returns (string) Returns the symbol of the token, e.g., `"MYT"`. -> **returns:** Symbol of the token +> **returns:** Symbol of the token. **`totalSupply` function** @@ -113,6 +111,8 @@ Get the total number of minted tokens. The total supply MUST be equal to the sum of the balances of all addresses—as returned by the `balanceOf` function. +The total supply MUST be equal to the sum of all the minted tokens as defined in all the `Minted` events minus the sum of all the burned tokens as defined in all the `Burned` events. + > **returns:** Total supply of tokens currently in circulation. **`balanceOf` function** @@ -126,7 +126,7 @@ Get the balance of the account with address `tokenHolder`. The balance MUST be zero (`0`) or higher. > **parameters** -> `tokenHolder`: Address for which the balance is returned +> `tokenHolder`: Address for which the balance is returned. > > **returns:** Amount of token held by `tokenHolder` in the token contract. @@ -144,7 +144,7 @@ The following rules MUST be applied regarding the *granularity*: - The *granularity* value MUST NOT be changed ever. - The *granularity* value MUST be greater or equal to `1`. - Any minting, send or burning of tokens MUST be a multiple of the *granularity* value. -- Any operation that would result in a balance that's not a multiple of the *granularity* value MUST be considered invalid, and the transaction MUST throw. +- Any operation that would result in a balance that's not a multiple of the *granularity* value MUST be considered invalid, and the transaction MUST `revert`. *NOTE*: Most of the tokens SHOULD be fully partitionable. I.e., this function SHOULD return `1` unless there is a good reason for not allowing any partition of the token. @@ -182,9 +182,43 @@ The following rules apply to any *operator*: - An address MUST always be an *operator* for itself. Hence an address MUST NOT ever be revoked as its own *operator*. - If an address is an *operator* for a *token holder*, `isOperatorFor` MUST return `true`. - If an address is not an *operator* for a *token holder*, `isOperatorFor` MUST return `false`. -- The *operator* MUST pass the `data` from a *token holder* as is when sending and burning tokens. If the *token holder* does not provide any `data`, the operator MUST pass either an empty `data` field or MUST NOT pass any data (depending on whether the function requires the *token holder*'s `data` or not). +- The token contract MUST emit an `AuthorizedOperator` event with the correct values when a *token holder* authorizes an address as its *operator* as defined in the [`AuthorizedOperator` Event][authorizedoperator]. +- The token contract MUST emit a `RevokedOperator` event with the correct values when a *token holder* revokes an address as its *operator* as defined in the [`RevokedOperator` Event][revokedoperator]. -*NOTE*: It is not expected for the token contract to validate the origin of the `data` field when the *operator* and *token holder* are different. However, the *operator* MUST not pass any information in the `data` field which does not originate from the *token holder*. +*NOTE*: A *token holder* MAY authorize an already authorized *operator*. An `AuthorizedOperator` MUST be emitted each time. + +*NOTE*: A *token holder* MAY revoke an already revoked *operator*. A `RevokedOperator` MUST be emitted each time. + +*NOTE*: A token holder MAY have multiple *operators* at the same time. + +**`AuthorizedOperator` event** + +``` solidity +event AuthorizedOperator(address indexed operator, address indexed tokenHolder) +``` + +Indicates the authorization of `operator` as an *operator* for `tokenHolder`. + +*NOTE*: This event MUST NOT be emitted outside of an *operator* authorization process. + +> **parameters** +> `operator`: Address which became an *operator* of `tokenHolder`. +> `tokenHolder`: Address of a token holder which authorized the `operator` address as an *operator*. + + +**`RevokedOperator` event** + +``` solidity +event RevokedOperator(address indexed operator, address indexed tokenHolder) +``` + +Indicates the revocation of `operator` as an *operator* for `tokenHolder`. + +*NOTE*: This event MUST NOT be emitted outside of an *operator* revocation process. + +> **parameters** +> `operator`: Address which was revoked as an *operator* of `tokenHolder`. +> `tokenHolder`: Address of a token holder which revoked the `operator` address as an *operator*. The `defaultOperators`, `authorizeOperator`, `revokeOperator` and `isOperatorFor` functions described below MUST be implemented to manage *operators*. Token contracts MAY implement other functions to manage *operators*. @@ -199,7 +233,7 @@ Get the list of *default operators* as defined by the token contract. *NOTE*: If the token contract does not have any *default operators*, this function MUST return an empty list. -> **returns:** List of addresses of all the *default operators* +> **returns:** List of addresses of all the *default operators*. **`authorizeOperator` function** @@ -207,36 +241,26 @@ Get the list of *default operators* as defined by the token contract. function authorizeOperator(address operator) public ``` -Set a third party `operator` address as an *operator* of `msg.sender` to send, burn, and mint tokens on its behalf. +Set a third party `operator` address as an *operator* of `msg.sender` to send and burn tokens on its behalf. -An `AuthorizedOperator` event MUST be emitted on a successful call to this function. - -*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to authorize the token holder (`msg.sender`) as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`). - -*NOTE*: A token holder MAY authorize multiple *operators* at the same time. +*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST `revert` if it is called to authorize the token holder (`msg.sender`) as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`). > **parameters** > `operator`: Address to set as an *operator* for `msg.sender`. - **`revokeOperator` function** ``` solidity function revokeOperator(address operator) public ``` -Remove the right of `operator` address to be an *operator* for `msg.sender` and to send and burn tokens on its behalf. +Remove the right of the `operator` address to be an *operator* for `msg.sender` and to send and burn tokens on its behalf. -Revoke a third party `operator`'s rights to send tokens on behalf of `msg.sender`. +*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST `revert` if it is called to revoke the token holder (`msg.sender`) as an *operator* for itself (i.e., if `operator` is equal to `msg.sender`). -A `RevokedOperator` event MUST be emitted on a successful call to this function. - -*NOTE*: The *token holder* (`msg.sender`) is always an *operator* for itself. This right SHALL NOT be revoked. Hence this function MUST throw if it is called to revoke the token holder (`msg.sender`) as an *operator* for itself (i.e., if `operator` is equal to `msg.sender`). - -> **parameters** +> **parameters** > `operator`: Address to rescind as an *operator* for `msg.sender`. - **`isOperatorFor` function** ``` solidity @@ -247,7 +271,7 @@ Indicate whether the `operator` address is an *operator* of the `tokenHolder` ad > **parameters** > `operator`: Address which may be an *operator* of `tokenHolder`. -> `tokenHolder`: Address of a token holder which may have the `operator` address as an `operator`. +> `tokenHolder`: Address of a token holder which may have the `operator` address as an *operator*. > > **returns:** `true` if `operator` is an *operator* of `tokenHolder` and `false` otherwise. @@ -262,29 +286,21 @@ When an *operator* sends an `amount` of tokens from a *token holder* to a *recip - The balance of the *recipient* MUST be increased by the `amount`. - The balance of the *token holder* MUST be greater or equal to the `amount`—such that its resulting balance is greater or equal to zero (`0`) after the send. - The token contract MUST emit a `Sent` event with the correct values as defined in the [`Sent` Event][sent]. -- The *token holder* MAY communicate any information in the `data`. - The *operator* MAY communicate any information in the `operatorData`. -- The token contract MUST NOT modify the `data` nor the `operatorData`. -- *Operators* MUST NOT modify the `data` provided by *token holders*. -- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* registers an `ERC777TokensRecipient` implementation via [ERC820]. -- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820]. -- When calling `tokensToSend`, `tokensReceived` or both: - - `operator` MUST be the address which initiated the send. (Generally the `msg.sender` of the send function call.) - - `from` MUST be the address of the *token holder*. - - `to` MUST be the address of the *recipient*. - - `data` MUST be the data provided by the *token holder*. - - `operatorData` MUST be the data provided by the *operator*. +- The token contract MUST call the `tokensToSend` hook of the *token holder* if the *token holder* registers an `ERC777TokensRecipient` implementation via [ERC820]. +- The token contract MUST call the `tokensReceived` hook of the *recipient* if the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820]. +- The `data` and `operatorData` MUST be immutable during the entire send process—hence the same `data` and `operatorData` MUST be used to call both hooks and emit the `Sent` event. -The token contract MUST throw when sending in any of the following cases: +The token contract MUST `revert` when sending in any of the following cases: -- The *operator* address is not an authorized operator for the *token holder* -- The resulting *token holder* balance or *recipient* balance after the send has a granularity smaller than the *granularity* defined by the token contract. +- The *operator* address is not an authorized operator for the *token holder*. +- The resulting *token holder* balance or *recipient* balance after the send is not a multiple of the *granularity* defined by the token contract. - The address of the *token holder* or the *recipient* is `0x0`. -- Any of the resulting balance becomes negative, i.e. becomes less than zero (`0`). +- Any of the resulting balances becomes negative, i.e. becomes less than zero (`0`). -The token contract MAY send tokens from many *token holders* to many *recipients* or both. In this case: +The token contract MAY send tokens from many *token holders*, to many *recipients*, or both. In this case: -- The previous send rules MUST apply to all the *token holders* and all the *recipients* +- The previous send rules MUST apply to all the *token holders* and all the *recipients*. - The sum of all the balances incremented MUST be equal to the total sent `amount`. - The sum of all the balances decremented MUST be equal to the total sent `amount`. - A `Sent` event MUST be emitted for every *token holder* and *recipient* pair with the corresponding amount for each pair. @@ -294,14 +310,14 @@ The token contract MAY send tokens from many *token holders* to many *recipients *NOTE*: Transfer of tokens MAY be chained. For example, if a contract upon receiving tokens sends them further to another address. In this case, the previous send rules apply to each send, in order. +*NOTE*: Sending an amount of zero (`0`) tokens is valid and MUST be treated as a regular send. + *Implementation Requirement*: - The token contract MUST call the `tokensToSend` hook *before* updating the state. - The token contract MUST call the `tokensReceived` hook *after* updating the state. I.e., `tokensToSend` MUST be called first, then the balances MUST be updated to reflect the send, and finally `tokensReceived` MUST be called *afterward*. Thus a `balanceOf` call within `tokensToSend` returns the balance of the address *before* the send and a `balanceOf` call within `tokensReceived` returns the balance of the address *after* the send. - -*NOTE*: The `data` and `operatorData` fields are free byte ranges provided by the *token holder* and the *operator* respectively. The `data` field is intended for the recipient. Typically, the recipient SHOULD indicate which data it expects to receive. (Similarly to the data field when sending ether.) The `operatorData` field is intended more for logging purposes and particular cases. (Examples include payment references, cheque numbers, countersignatures and more.) In most of the cases the recipient would ignore the `operatorData`, or at most, it would log the `operatorData`. - +*NOTE*: The `data` field contains extra information intended for, and defined by the recipient— similar to the data field in a regular ether send transaction. Typically, `data` is used to describe the intent behind the send. The `operatorData` MUST only be provided by the *operator*. It is intended more for logging purposes and particular cases. (Examples include payment references, cheque numbers, countersignatures and more.) In most of the cases the recipient would ignore the `operatorData`, or at most, it would log the `operatorData`. **`Sent` event** @@ -314,12 +330,12 @@ Indicate a send of `amount` of tokens from the `from` address to the `to` addres *NOTE*: This event MUST NOT be emitted outside of a send or an [ERC20] transfer process. > **parameters** -> `operator`: address which triggered the send -> `from`: token holder -> `to`: token recipient -> `amount`: number of tokens to send -> `data`: information attached to the send by the token holder (`from`) -> `operatorData`: information attached to the send by the `operator` +> `operator`: Address which triggered the send. +> `from`: Token holder. +> `to`: Token recipient. +> `amount`: Number of tokens to send. +> `data`: Information attached to the send, and intended for the recipient (`to`). +> `operatorData`: Information attached to the send by the `operator`. The `send` and `operatorSend` functions described below MUST be implemented to send tokens. Token contracts MAY implement other functions to send tokens. @@ -337,9 +353,9 @@ Send the `amount` of tokens from the address `msg.sender` to the address `to`. The *operator* and the *token holder* MUST both be the `msg.sender`. > **parameters** -> `to`: token recipient -> `amount`: number of tokens to send -> `data`: information attached to the send by the *token holder* +> `to`: Token recipient. +> `amount`: Number of tokens to send. +> `data`: Information attached to the send, and intended for the recipient (`to`). **`operatorSend` function** @@ -349,20 +365,18 @@ function operatorSend(address from, address to, uint256 amount, bytes data, byte Send the `amount` of tokens on behalf of the address `from` to the address `to`. -The *operator* MUST be `msg.sender`. +The *operator* MUST be `msg.sender`. The value of `from` MAY be `0x0`, then the `from` (*token holder*) used for the send MUST be `msg.sender` (the `operator`). -*Reminder*: If the *operator* address is not an authorized operator of the `from` address the send MUST throw. - -*NOTE*: The *operator* MAY pass any information via `operatorData`. The *operator* MUST only pass data given to it by the *token holder* to the `data` parameter. The token holder MAY provide this data to the *operator* beforehand through another medium. +*Reminder*: If the *operator* address is not an authorized operator of the `from` address, then the send process MUST `revert`. *NOTE*: `from` and `msg.sender` MAY be the same address. I.e., an address MAY call `operatorSend` for itself. This call MUST be equivalent to `send` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `send` function). > **parameters** -> `from`: token holder -> `to`: token recipient -> `amount`: number of tokens to send -> `data`: information attached to the send, previously provided by the *token holder* -> `operatorData`: information attached to the send by the `operator` +> `from`: Token holder (or `0x0` to set `from` to `msg.sender`). +> `to`: Token recipient. +> `amount`: Number of tokens to send. +> `data`: Information attached to the send, and intended for the recipient (`to`). +> `operatorData`: Information attached to the send by the `operator`. #### **Minting Tokens** @@ -375,17 +389,12 @@ Nonetheless, the rules below MUST be respected when minting for a *recipient*: - The balance of `0x0` MUST NOT be decreased. - The balance of the *recipient* MUST be increased by the amount of tokens minted. - The token contract MUST emit a `Minted` event with the correct values as defined in the [`Minted` Event][minted]. -- The token contract MUST call the `tokensReceived` hook of the *recipient*, provided the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820]. -- When calling `tokensReceived`: - - `operator` MUST be the address which initiated the minting action. (Generally the `msg.sender` of the minting function call.) - - `from` MUST be `0x0`. - - `to` MUST be the address of the *recipient*. - - `data` MUST be empty. - - `operatorData` MUST contain the data, provided by the address which initiated the minting action (i.e., the *operator*). +- The token contract MUST call the `tokensReceived` hook of the *recipient* if the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820]. +- The `data` and `operatorData` MUST be immutable during the entire mint process—hence the same `data` and `operatorData` MUST be used to call the `tokensReceived` hook and emit the `Minted` event. -The token contract MUST throw when minting in any of the following cases: +The token contract MUST `revert` when minting in any of the following cases: -- The resulting *recipient* balance after the mint has a granularity smaller than the *granularity* defined by the token contract. +- The resulting *recipient* balance after the mint is not a multiple of the *granularity* defined by the token contract. - The *recipient* is a contract, and it does not implement the `ERC777TokensRecipient` interface via [ERC820]. - The address of the *recipient* is `0x0`. @@ -399,10 +408,14 @@ The token contract MAY mint tokens for multiple *recipients* at once. In this ca - A `Minted` event MUST be emitted for every *recipient* with the corresponding amount for each *recipient*. - The sum of all the amounts from the `Minted` event MUST be equal to the total minted `amount`. +*NOTE*: Minting an amount of zero (`0`) tokens is valid and MUST be treated as a regular mint. + +*NOTE*: The `data` field contains extra information intended for, and defined by the recipient— similar to the data field in a regular ether send transaction. Typically, `data` is used to describe the intent behind the mint. The `operatorData` MUST only be provided by the *operator*. It is intended more for logging purposes and particular cases. (Examples include payment references, cheque numbers, countersignatures and more.) In most of the cases the recipient would ignore the `operatorData`, or at most, it would log the `operatorData`. + **`Minted` event** ``` solidity -event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData) +event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData) ``` Indicate the minting of `amount` of tokens to the `to` address by the `operator` address. @@ -410,10 +423,11 @@ Indicate the minting of `amount` of tokens to the `to` address by the `operator` *NOTE*: This event MUST NOT be emitted outside of a mint process. > **parameters** -> `operator`: address which triggered the mint -> `to`: token recipient -> `amount`: number of tokens minted -> `operatorData`: information attached to the minting by the `operator` +> `operator`: Address which triggered the mint. +> `to`: Token recipient. +> `amount`: Number of tokens minted. +> `data`: Information attached to the minting, and intended for the recipient (`to`). +> `operatorData`: Information attached to the minting by the `operator`. #### **Burning Tokens** @@ -426,24 +440,19 @@ The rules below MUST be respected when burning the tokens of a *token holder*: - The balance of `0x0` MUST NOT be increased. - The balance of the *token holder* MUST be decreased by amount of tokens burned. - The token contract MUST emit a `Burned` event with the correct values as defined in the [`Burned` Event][burned]. -- The token contract MUST call the `tokensToSend` hook of the *token holder*, provided the *token holder* registers an `ERC777TokensSender` implementation via [ERC820]. -- When calling `tokensToSend`: - - `operator` MUST be the address which initiated the burning action. (Generally the `msg.sender` of the burning function call.) - - `from` MUST be the address of the *token holder*. - - `to` MUST be `0x0`. - - `data` MUST be the data provided by the *token holder*. - - `operatorData` MUST contain data, provided by the address which initiated the burning action (i.e., the *operator*). +- The token contract MUST call the `tokensToSend` hook of the *token holder* if the *token holder* registers an `ERC777TokensSender` implementation via [ERC820]. +- The `operatorData` MUST be immutable during the entire burn process—hence the same `operatorData` MUST be used to call the `tokensToSend` hook and emit the `Burned` event. +- The `data` field of the `tokensToSend` hook MUST be empty. -The token contract MUST throw when burning in any of the following cases: +The token contract MUST `revert` when burning in any of the following cases: -- The address initiating the burn of tokens from a *token holder* MUST be an *operator* of the *token holder*. - -- The resulting *token holder* balance after the burn has a granularity smaller than the *granularity* defined by the token contract. +- The *operator* address is not an authorized operator for the *token holder*. +- The resulting *token holder* balance after the burn is not a multiple of the *granularity* defined by the token contract. - The balance of *token holder* is inferior to the amount of tokens to burn (i.e., resulting in a negative balance for the *token holder*). - The address of the *token holder* is `0x0`. *[ERC20] compatibility requirement*: -While a `Sent` event MUST NOT be emitted when burning, if the token contract is [ERC20] enabled, a `Transfer` event with the `to` parameter set to `0x0` MUST be emitted as defined in the [ERC20] standard. +While a `Sent` event MUST NOT be emitted when burning; if the token contract is [ERC20] enabled, a `Transfer` event with the `to` parameter set to `0x0` SHOULD be emitted. The [ERC20] standard does not define the concept of burning tokens, but this is a commonly accepted practice. The token contract MAY burn tokens for multiple *token holders* at once. In this case: @@ -452,10 +461,12 @@ The token contract MAY burn tokens for multiple *token holders* at once. In this - A `Burned` event MUST be emitted for every *token holder* with the corresponding amount for each *token holder*. - The sum of all the amounts from the `Burned` event MUST be equal to the total burned `amount`. +*NOTE*: Burning an amount of zero (`0`) tokens is valid and MUST be treated as a regular burn. + **`Burned` event** ``` solidity -event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData) +event Burned(address indexed operator, address indexed from, uint256 amount, bytes operatorData) ``` Indicate the burning of `amount` of tokens from the `from` address by the `operator` address. @@ -463,11 +474,10 @@ Indicate the burning of `amount` of tokens from the `from` address by the `opera *NOTE*: This event MUST NOT be emitted outside of a burn process. > **parameters** -> `operator`: address which triggered the burn -> `from`: token holder -> `amount`: number of tokens burned -> `data`: information attached to the burn by the token holder -> `operatorData`: information attached to the burn by the `operator` +> `operator`: Address which triggered the burn. +> `from`: Token holder whose tokens are burned. +> `amount`: Number of tokens burned. +> `operatorData`: Information attached to the burn by the `operator`. The `burn` and `operatorBurn` functions described below MUST be implemented to burn tokens. Token contracts MAY implement other functions to burn tokens. @@ -475,7 +485,7 @@ Token contracts MAY implement other functions to burn tokens. **`burn` function** ``` solidity -function burn(uint256 amount, bytes data) public; +function burn(uint256 amount) public; ``` Burn the `amount` of tokens from the address `msg.sender`. @@ -483,22 +493,26 @@ Burn the `amount` of tokens from the address `msg.sender`. The *operator* and the *token holder* MUST both be the `msg.sender`. > **parameters** -> `amount`: number of tokens to burn -> `data`: information attached to the burn by the token holder +> `amount`: Number of tokens to burn. **`operatorBurn` function** ``` solidity -function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) public; +function operatorBurn(address from, uint256 amount, bytes operatorData) public; ``` Burn the `amount` of tokens on behalf of the address `from`. -The *operator* MUST be `msg.sender`. +The *operator* MUST be `msg.sender`. The value of `from` MAY be `0x0`, then the `from` (*token holder*) used for the burn MUST be `msg.sender` (the `operator`). -*Reminder*: If the *operator* address is not an authorized operator of the `from` address the burn MUST throw. +*Reminder*: If the *operator* address is not an authorized operator of the `from` address, then the burn process MUST `revert`. -*NOTE*: The *operator* MAY pass any information via `operatorData`. The *operator* MUST only pass to `data` data given to it by the *token holder*. The token holder MAY provide this data to the *operator* beforehand through another medium. +> **parameters** +> `from`: Token holder whose tokens will be burned (or `0x0` to set `from` to `msg.sender`). +> `amount`: Number of tokens to burn. +> `operatorData`: Information attached to the burn by the *operator*. + +*NOTE*: The *operator* MAY pass any information via `operatorData`. The `operatorData` MUST only be provided by the *operator*. *NOTE*: `from` and `msg.sender` MAY be the same address. I.e., an address MAY call `operatorBurn` for itself. This call MUST be equivalent to `burn` with the addition that the *operator* MAY specify an explicit value for `operatorData` (which cannot be done with the `burn` function). @@ -512,7 +526,7 @@ interface ERC777TokensSender { address operator, address from, address to, - uint value, + uint256 amount, bytes data, bytes operatorData ) public; @@ -524,7 +538,7 @@ interface ERC777TokensSender { **`tokensToSend`** ``` solidity -function tokensToSend(address operator, address from, address to, uint value, bytes data, bytes operatorData) public +function tokensToSend(address operator, address from, address to, uint256 amount, bytes data, bytes operatorData) public ``` Notify a send or burn (if `to` is `0x0`) of `amount` tokens from the `from` address to the `to` address by the `operator` address. @@ -532,14 +546,14 @@ Notify a send or burn (if `to` is `0x0`) of `amount` tokens from the `from` addr *NOTE*: This function MUST NOT be called outside of a burn, send or [ERC20] transfer process. > **parameters** -> `operator`: address which triggered the balance decrease (through sending or burning) -> `from`: *token holder* -> `to`: *token recipient* for a send and `0x` for a burn -> `amount`: number of tokens the *token holder* balance is decreased by -> `data`: extra information provided by the *token holder* -> `operatorData`: extra information provided by the address which triggered the balance decrease +> `operator`: Address which triggered the balance decrease (through sending or burning). +> `from`: *token holder*. +> `to`: *token recipient* for a send and `0x` for a burn. +> `amount`: Number of tokens the *token holder* balance is decreased by. +> `data`: Extra information provided by the *token holder*. +> `operatorData`: Extra information provided by the address which triggered the balance decrease. -The following rules apply to the `tokensToSend` hook: +The following rules apply when calling the `tokensToSend` hook: - The `tokensToSend` hook MUST be called every time the balance is decremented. - The `tokensToSend` hook MUST be called *before* the state is updated—i.e. *before* the balance is decremented. @@ -547,9 +561,11 @@ The following rules apply to the `tokensToSend` hook: - `from` MUST be the address of the *token holder* whose balance is decreased. - `to` MUST be the address of the *recipient* whose balance is increased for a send. - `to` MUST be `0x0` for a burn. -- `data` MUST contain the extra information provided by the *token holder* (if any) for a send or burn. +- `amount` MUST be the number of tokens the *token holder* balance is decreased by. +- `data` MUST contain the extra information provided by the *token holder* (if any) for a send. +- `data` MUST be empty for a burn. - `operatorData` MUST contain the extra information provided by the address which triggered the decrease of the balance (if any). -- The *token holder* MAY block a decrease of its balance by throwing. (I.e., reject the withdrawal of tokens from its account.) +- The *token holder* MAY block a decrease of its balance by `revert`ing. (I.e., reject the withdrawal of tokens from its account.) *NOTE*: Multiple *token holders* MAY use the same implementation of `ERC777TokensSender`. @@ -560,7 +576,7 @@ This hook takes precedence over [ERC20] and MUST be called (if registered) when #### **`ERC777TokensRecipient` And The `tokensReceived` Hook** -The `tokensReceived` hook notifies of any increment of the balance (send and mint) for a given *recipient*. Any address (regular or contract) wishing to be notified of token debits from their address MAY register the address of a contract implementing the `ERC777TokensSender` interface described below via [ERC820]. +The `tokensReceived` hook notifies of any increment of the balance (send and mint) for a given *recipient*. Any address (regular or contract) wishing to be notified of token credits to their address MAY register the address of a contract implementing the `ERC777TokensSender` interface described below via [ERC820]. ``` solidity interface ERC777TokensRecipient { @@ -568,7 +584,7 @@ interface ERC777TokensRecipient { address operator, address from, address to, - uint amount, + uint256 amount, bytes data, bytes operatorData ) public; @@ -577,7 +593,7 @@ interface ERC777TokensRecipient { If the *recipient* is a contract, which has not registered an `ERC777TokensRecipient` implementation; the token contract: -- MUST throw if the `tokensReceived` hook is called from a mint or send call. +- MUST `revert` if the `tokensReceived` hook is called from a mint or send call. - SHOULD accept if the `tokensReceived` hook is called from an ERC20 `transfer` or `transferFrom` call. *NOTE*: A regular address MAY register a different address—the address of a contract—implementing the interface on its behalf. A contract MUST register either its address or the address of another contract but said address MUST implement the interface on its behalf. @@ -585,7 +601,7 @@ If the *recipient* is a contract, which has not registered an `ERC777TokensRecip **`tokensReceived`** ``` solidity -function tokensReceived(address operator, address from, address to, uint amount, bytes data, bytes operatorData) public +function tokensReceived(address operator, address from, address to, uint256 amount, bytes data, bytes operatorData) public ``` Notify a send or mint (if `from` is `0x0`) of `amount` tokens from the `from` address to the `to` address by the `operator` address. @@ -593,25 +609,25 @@ Notify a send or mint (if `from` is `0x0`) of `amount` tokens from the `from` ad *NOTE*: This function MUST NOT be called outside of a mint, send or [ERC20] transfer process. > **parameters** -> `operator`: address which triggered the balance increase (through sending or minting) -> `from`: *token holder* for a send and `0x` for a mint -> `to`: *token recipient* -> `amount`: number of tokens the *recipient* balance is increased by -> `data`: extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint, -> `operatorData`: extra information provided by the address which triggered the balance increase +> `operator`: Address which triggered the balance increase (through sending or minting). +> `from`: *token holder* for a send and `0x` for a mint. +> `to`: *token recipient*. +> `amount`: Number of tokens the *recipient* balance is increased by. +> `data`: Extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint,. +> `operatorData`: Extra information provided by the address which triggered the balance increase. -The following rules apply to the `tokensToSend` hook: +The following rules apply when calling the `tokensToSend` hook: - The `tokensReceived` hook MUST be called every time the balance is incremented. -- The `tokensReceived` hook MUST be called *after* the state is update—i.e. *after* the balance is incremented. +- The `tokensReceived` hook MUST be called *after* the state is updated—i.e. *after* the balance is incremented. - `operator` MUST be the address which triggered the increase of the balance. - `from` MUST be the address of the *token holder* whose balance is decreased for a send. - `from` MUST be `0x0` for a mint. - `to` MUST be the address of the *recipient* whose balance is increased. -- `data` MUST contain the extra information provided by the *token holder* (if any) for a send. -- `data` MUST be empty for a mint. +- `amount` MUST be the number of tokens the *recipient* balance is increased by. + - `operatorData` MUST contain the extra information provided by the address which triggered the increase of the balance (if any). -- The *token holder* MAY block an increase of its balance by throwing. (I.e., reject the reception of tokens.) +- The *token holder* MAY block an increase of its balance by `revert`ing. (I.e., reject the reception of tokens.) *NOTE*: Multiple *token holders* MAY use the same implementation of `ERC777TokensRecipient`. @@ -640,7 +656,7 @@ The logo for the standard can be found in the [`/assets/eip-777/logo`][logos] fo ## Rationale -This standard solves some of the shortcomings of [ERC20] while maintaining backward compatibility with [ERC20]. It avoids the problems and vulnerabilities of he [EIP223]. +This standard solves some of the shortcomings of [ERC20] while maintaining backward compatibility with [ERC20]. It avoids the problems and vulnerabilities of [EIP223]. It goes a step further by allowing *operators* (generally contracts) which can manage the tokens in the same way that the [ERC20] with infinite `approve` was allowed. Finally, it adds hooks to provide further control to *token holders* over their tokens. Note that, the usage of [ERC820] provides backward compatibility with wallets and existing contracts without having to be redeployed thanks proxy contracts implementing the hooks. @@ -660,8 +676,7 @@ The state-modifying functions from both standards are decoupled and can operate If the token implements [ERC20], it MUST register the `ERC20Token` interface with its own address via [ERC820]. If the contract has a switch to enable or disable [ERC20] functions, every time the switch is triggered, the token MUST register or unregister its own address accordingly the `ERC20Token` interface via [ERC820]. (Unregistering implies setting the address to `0x0`.) -The difference for new contracts implementing [ERC20] is that -`tokensToSend` and `tokensReceived` hooks take precedence over [ERC20]. Even with an [ERC20] `transfer` and `transferFrom` call, the token contract MUST check via [ERC820] if the `from` and the `to` address implement `tokensToSend` and `tokensReceived` hook respectively. If any hook is implemented, it MUST be called. Note that when calling [ERC20] `transfer` on a contract, if the contract does not implement `tokensReceived`, the `transfer` call SHOULD still be accepted even if this means the tokens will probably be locked. +The difference for new contracts implementing [ERC20] is that `tokensToSend` and `tokensReceived` hooks take precedence over [ERC20]. Even with an [ERC20] `transfer` and `transferFrom` call, the token contract MUST check via [ERC820] if the `from` and the `to` address implement `tokensToSend` and `tokensReceived` hook respectively. If any hook is implemented, it MUST be called. Note that when calling [ERC20] `transfer` on a contract, if the contract does not implement `tokensReceived`, the `transfer` call SHOULD still be accepted even if this means the tokens will probably be locked. The table below summarizes the different actions the token contract MUST take when sending, minting and transferring token via [ERC777] and [ERC20]: @@ -695,13 +710,13 @@ The table below summarizes the different actions the token contract MUST take wh contract - MUST throw + MUST revert -There is no particular action to take if `tokensToSend` is not implemented. The transfer MUST proceed and only be canceled if another condition is not respected such as lack of funds or a throw in `tokensReceived` (if present). +There is no particular action to take if `tokensToSend` is not implemented. The transfer MUST proceed and only be canceled if another condition is not respected such as lack of funds or a `revert` in `tokensReceived` (if present). -During a send, mint and burn, the respective `Sent`, `Minted` and `Burned` events MUST be emitted. Furthermore, if the token contract declares that it implements `ERC20Token` via [ERC820], the token contract SHOULD emit a `Transfer` event for minting and MUST emit a `Transfer` event for sending and burning (as specified in the [ERC20] standard). During an [ERC20]'s `transfer` or `transferFrom` functions, a valid `Sent` event MUST be emitted. +During a send, mint and burn, the respective `Sent`, `Minted` and `Burned` events MUST be emitted. Furthermore, if the token contract declares that it implements `ERC20Token` via [ERC820], the token contract SHOULD emit a `Transfer` event for minting and burning and MUST emit a `Transfer` event for sending (as specified in the [ERC20] standard). During an [ERC20]'s `transfer` or `transferFrom` functions, a valid `Sent` event MUST be emitted. Hence for any movement of tokens, two events MAY be emitted: an [ERC20] `Transfer` and an [ERC777] `Sent`, `Minted` or `Burned` (depending on the type of movement). Third-party developers MUST be careful not to consider both events as separate movements. As a general rule, if an application considers the token as an ERC20 token, then only the `Transfer` event MUST be taken into account. If the application considers the token as an ERC777 token, then only the `Sent`, `Minted` and `Burned` events MUST be considered. @@ -711,7 +726,8 @@ The [repository with the reference implementation][jacquesd/ERC777] contains all ## Implementation -The GitHub repository [jacquesd/ERC777] contains the [reference implementation]. +The GitHub repository [jacquesd/ERC777] contains the [reference implementation]. The reference implementation is also available via [npm][npm/erc777] and can be installed with `npm install erc777`. + ## Copyright @@ -724,11 +740,14 @@ Copyright and related rights waived via [CC0]. [ERC777]: https://eips.ethereum.org/EIPS/eip-777 [ERC820]: https://eips.ethereum.org/EIPS/eip-820 [jacquesd/ERC777]: https://github.com/jacquesd/ERC777 +[npm/erc777]: https://www.npmjs.com/package/erc777 [ref tests]: https://github.com/jacquesd/ERC777/blob/master/test/ReferenceToken.test.js [reference implementation]: https://github.com/jacquesd/ERC777/blob/master/contracts/examples/ReferenceToken.sol [EIP223]: https://github.com/ethereum/EIPs/issues/223 [eth_estimateGas]: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_estimategas +[authorizedoperator]: #authorizedoperator +[revokedoperator]: #revokedoperator [isOperatorFor]: #isOperatorFor [defaultOperators]: #defaultOperators [sent]: #sent From cd59c3477cd2cda1d3e377b4f41f7b92a852f67e Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Tue, 31 Jul 2018 17:46:54 +0200 Subject: [PATCH 081/177] Automatically merged updates to draft EIP(s) 820 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-820.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-820.md b/EIPS/eip-820.md index f00841ab..1cc8eff5 100644 --- a/EIPS/eip-820.md +++ b/EIPS/eip-820.md @@ -1,7 +1,7 @@ --- eip: 820 title: Pseudo-introspection registry contract -author: Jordi Baylina +author: Jordi Baylina , Jacques Dafflon discussions-to: https://github.com/ethereum/EIPs/issues/820 status: Draft type: Standards Track From 7bccb190f5dcb61c65a92a40c3388d778e2f3c29 Mon Sep 17 00:00:00 2001 From: Mark Beylin Date: Tue, 31 Jul 2018 17:14:19 -0400 Subject: [PATCH 082/177] Standard Bounties (#1081) * added Standard Bounties EIP * updated date and EIP number * updated EIP number * Updated to follow the prescribed EIP-X format * renamed file * Updated based on suggestions * updated to lowercase eip * Update eip-1081.md * Updated EIP verbiage. --- EIPS/eip-1081.md | 125 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 EIPS/eip-1081.md diff --git a/EIPS/eip-1081.md b/EIPS/eip-1081.md new file mode 100644 index 00000000..3202420d --- /dev/null +++ b/EIPS/eip-1081.md @@ -0,0 +1,125 @@ +--- +eip: 1081 +Title: Standard Bounties +Authors: Mark Beylin , Kevin Owocki , Ricardo Guilherme Schmidt (@3esmit) +Discussions-to: https://gitter.im/bounties-network/Lobby +Status: Draft +Type: Standards Track +Category: ERC +Created: 2018-05-14 +Requires: 20 +--- + +## Simple Summary + +A standard contract and interface for issuing bounties on Ethereum, usable for any type of task, paying in any ERC20 token or in ETH. + +## Abstract + +In order to encourage cross-platform interoperability of bounties on Ethereum, and for easier reputational tracking, StandardBounties can facilitate the administration of funds in exchange for deliverables corresponding to a completed task, in a publicly auditable and immutable fashion. + +## Motivation +In the absence of a standard for bounties on Ethereum, it would be difficult for platforms to collaborate and share the bounties which users create (thereby recreating the walled gardens which currently exist on Web2.0 task outsourcing platforms). A standardization of these interactions across task types also makes it far easier to track various reputational metrics (such as how frequently you pay for completed submissions, or how frequently your work gets accepted). + +## Specification +After studying bounties as they've existed for thousands of years (and after implementing and processing over 300 of them on main-net in beta), we've discovered that there are 3 core steps to every bounty: +- a bounty is **issued**: an `issuer` specifies the requirements for the task, describing the desired outcome, and how much they would be willing to pay for the completion of that task (denoted in one or several tokens). +- a bounty is **fulfilled**: a bounty `fulfiller` may see the bounty, complete the task, and produce a deliverable which is itself the desired outcome of the task, or simply a record that it was completed. Hashes of these deliverables should be stored immutably on-chain, to serve as proof after the fact. +- a fulfillment is **accepted**: a bounty `issuer` or `arbiter` may select one or more submissions to be accepted, thereby releasing payment to the bounty fulfiller(s), and transferring ownership over the given deliverable to the `issuer`. + +To implement these steps, a number of functions are needed: +- `initializeBounty(address _issuer, address _arbiter, string _data, uint _deadline)`: This is used when deploying a new StandardBounty contract, and is particularly useful when applying the proxy design pattern, whereby bounties cannot be initialized in their constructors. Here, the data string should represent an IPFS hash, corresponding to a JSON object which conforms to the schema (described below). +- `fulfillBounty(address[] _fulfillers, uint[] _numerators, uint _denomenator, string _data)`: This is called to submit a fulfillment, submitting a string representing an IPFS hash which contains the deliverable for the bounty. Initially fulfillments could only be submitted by one individual at a time, however users consistently told us they desired to be able to collaborate on fulfillments, thereby allowing the credit for submissions to be shared by several parties. The lines along which eventual payouts are split are determined by the fractions of the submission credited to each fulfiller (using the array of numerators and single denominator). Here, a bounty platform may also include themselves as a collaborator to collect a small fee for matching the bounty with fulfillers. +- `acceptFulfillment(uint _fulfillmentId, StandardToken[] _payoutTokens, uint[] _tokenAmounts)`: This is called by the `issuer` or the `arbiter` to pay out a given fulfillment, using an array of tokens, and an array of amounts of each token to be split among the contributors. This allows for the bounty payout amount to move as it needs to based on incoming contributions (which may be transferred directly to the contract address). It also allows for the easy splitting of a given bounty's balance among several fulfillments, if the need should arise. + - `drainBounty(StandardToken[] _payoutTokens)`: This may be called by the `issuer` to drain a bounty of it's funds, if the need should arise. +- `changeBounty(address _issuer, address _arbiter, string _data, uint _deadline)`: This may be called by the `issuer` to change the `issuer`, `arbiter`, `data`, and `deadline` fields of their bounty. +- `changeIssuer(address _issuer)`: This may be called by the `issuer` to change to a new `issuer` if need be +- `changeArbiter(address _arbiter)`: This may be called by the `issuer` to change to a new `arbiter` if need be +- `changeData(string _data)`: This may be called by the `issuer` to change just the `data` +- `changeDeadline(uint _deadline)`: This may be called by the `issuer` to change just the `deadline` + +Optional Functions: +- `acceptAndFulfill(address[] _fulfillers, uint[] _numerators, uint _denomenator, string _data, StandardToken[] _payoutTokens, uint[] _tokenAmounts)`: During the course of the development of this standard, we discovered the desire for fulfillers to avoid paying gas fees on their own, entrusting the bounty's `issuer` to make the submission for them, and at the same time accept it. This is useful since it still immutably stores the exchange of tokens for completed work, but avoids the need for new bounty fulfillers to have any ETH to pay for gas costs in advance of their earnings. +- `changeMasterCopy(StandardBounty _masterCopy)`: For `issuer`s to be able to change the masterCopy which their proxy contract relies on, if the proxy design pattern is being employed. +- `refundableContribute(uint[] _amounts, StandardToken[] _tokens)`: While non-refundable contributions may be sent to a bounty simply by transferring those tokens to the address where it resides, one may also desire to contribute to a bounty with the option to refund their contribution, should the bounty never receive a correct submission which is paid out. +`refundContribution(uint _contributionId)`: If a bounty hasn't yet paid out to any correct submissions and is past it's deadline, those individuals who employed the `refundableContribute` function may retreive their funds from the contract. + +**Schemas** +Persona Schema: +``` +{ + name: // optional - A string representing the name of the persona + email: // optional - A string representing the preferred contact email of the persona + githubUsername: // optional - A string representing the github username of the persona + address: // required - A string web3 address of the persona +} +``` +Bounty issuance `data` Schema: +``` +{ + payload: { + title: // A string representing the title of the bounty + description: // A string representing the description of the bounty, including all requirements + issuer: { + // persona for the issuer of the bounty + }, + funders:[ + // array of personas of those who funded the issue. + ], + categories: // an array of strings, representing the categories of tasks which are being requested + created: // the timestamp in seconds when the bounty was created + tokenSymbol: // the symbol for the token which the bounty pays out + tokenAddress: // the address for the token which the bounty pays out (0x0 if ETH) + + // ------- add optional fields here ------- + sourceFileName: // A string representing the name of the file + sourceFileHash: // The IPFS hash of the file associated with the bounty + sourceDirectoryHash: // The IPFS hash of the directory which can be used to access the file + webReferenceURL: // The link to a relevant web reference (ie github issue) + }, + meta: { + platform: // a string representing the original posting platform (ie 'gitcoin') + schemaVersion: // a string representing the version number (ie '0.1') + schemaName: // a string representing the name of the schema (ie 'standardSchema' or 'gitcoinSchema') + } +} +``` +Bounty `fulfillment` data Schema: + +``` +{ + payload: { + description: // A string representing the description of the fulfillment, and any necessary links to works + sourceFileName: // A string representing the name of the file being submitted + sourceFileHash: // A string representing the IPFS hash of the file being submitted + sourceDirectoryHash: // A string representing the IPFS hash of the directory which holds the file being submitted + fulfillers: { + // personas for the individuals whose work is being submitted + } + + // ------- add optional fields here ------- + }, + meta: { + platform: // a string representing the original posting platform (ie 'gitcoin') + schemaVersion: // a string representing the version number (ie '0.1') + schemaName: // a string representing the name of the schema (ie 'standardSchema' or 'gitcoinSchema') + } +} +``` +## Rationale + +The development of this standard began a year ago, with the goal of encouraging interoperability among bounty implementations on Ethereum. The initial version had significantly more restrictions: a bounty's `data` could not be changed after issuance (it seemed unfair for bounty `issuer`s to change the requirements after work is underway), and the bounty payout could not be changed (all funds needed to be deposited in the bounty contract before it could accept submissions). + +The initial version was also far less extensible, and only allowed for fixed payments to a given set of fulfillments. This new version makes it possible for funds to be split among several correct submissions, for submissions to be shared among several contributors, and for payouts to not only be in a single token as before, but in as many tokens as the `issuer` of the bounty desires. These design decisions were made after the 8+ months which Gitcoin, the Bounties Network, and Status Open Bounty have been live and meaningfully facilitating bounties for repositories in the Web3.0 ecosystem. + +## Test Cases + +Tests for our implementation can be found here: https://github.com/Bounties-Network/StandardBounties/tree/develop/test + +## Implementation + +A reference implementation can be found here: https://github.com/Bounties-Network/StandardBounties/blob/develop/contracts/StandardBounty.sol +**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/). From 2cf76d14366c8ed6bd7e69c1f9c19c8fd41f2649 Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Wed, 1 Aug 2018 01:49:57 -0700 Subject: [PATCH 083/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 231 ++++++++++++++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 87 deletions(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index 19820adc..2b5c5350 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -11,35 +11,43 @@ requires: 721, 165 --- ## Simple Summary +An extension of the [ERC721 standard](https://eips.ethereum.org/EIPS/eip-721) to enable ERC721 tokens to own other ERC721 tokens and ERC20 tokens. + +An extension of the [ERC20](https://eips.ethereum.org/EIPS/eip-20) and [ERC223](https://github.com/ethereum/EIPs/issues/223) standards to enable ERC20 and ERC223 tokens to be owned by ERC721 tokens. + + +This specification covers four different kinds of composable tokens: + +1. [ERC998ERC721 top-down composable tokens that receive, hold and transfer ERC721 tokens](#erc721-top-down-composable) +2. [ERC998ERC20 top-down composable tokens that receive, hold and transfer ERC20 tokens](#erc20-top-down-composable) +3. [ERC998ERC721 bottom-up composable tokens that attach themselves to other ERC721 tokens.](#erc721-bottom-up-composable) +4. [ERC998ERC20 bottom-up composable tokens that attach themselves to ERC721 tokens.](#erc20-bottom-up-composable) -An extension of the [ERC721 standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) to enable ERC721 tokens to own other ERC721 tokens and ERC20 tokens. ## Abstract -An ERC988 composable is an ERC721 token with additional functionality for owning or being owned by other ERC721 tokens. An ERC998 composable can also own ERC20 tokens. - -An ERC721 token owns another token if the the ownership of the token has been transferred to it. +1. An ERC988ERC721 top-down composable is an ERC721 token with additional functionality for owning other ERC721 tokens. +2. An ERC998ERC20 top-down composable is an ERC721 token with additional functionality for owning ERC20 tokens. +3. An ERC998ERC721 bottom-up composable is an ERC721 token with additional functionality for being owned by an ERC721 token. +4. An ERC998ERC20 bottom-up composable is an ERC20 token with additional functionality for being owned by an ERC721 token. A top-down composable contract stores and keeps track of child tokens for each of its tokens. A bottom-up composable contract stores and keeps track of a parent token for each its tokens. -With either kind of composable it is possible to compose lists or trees of ERC721 tokens connected by ownership. Any such structure will have a single owner address at the root of the structure that is the owner of the entire composition. The entire composition can be transferred with one transaction by changing the root owner. +With composable tokens it is possible to compose lists or trees of ERC721 and ERC20 tokens connected by ownership. Any such structure will have a single owner address at the root of the structure that is the owner of the entire composition. The entire composition can be transferred with one transaction by changing the root owner. -Both kinds of composable, top-down and bottom-up, have their advantages and disadvantages which are explained in the Rational section. It is possible for a composable token to be one or both kinds of composable. +Different composables, top-down and bottom-up, have their advantages and disadvantages which are explained in the [Rational section](#rationale). It is possible for a token to be one or more kinds of composable token. ## Specification -This specification specifies: - -1. [ERC721 top-down composable tokens that receive, hold and transfer ERC721 tokens](#erc721-top-down-composable) -2. [ERC20 top-down composable tokens that receive, hold and transfer ERC20 tokens](#erc20-top-down-composable) -3. [ERC721 bottom-up composable tokens that attach themselves to other ERC721 tokens.](#erc721-bottom-up-composable) -4. [ERC20 bottom-up composable tokens that attach themselves to other ERC20 tokens.](#erc20-bottom-up-composable) - ### ERC721 -Both ERC721 top-down and ERC721 bottom-up composable contracts must implement the [ERC721 interface](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md). +ERC998ERC721 top-down, ERC998ERC20 top-down, and ERC998ERC721 bottom-up composable contracts must implement the [ERC721 interface](https://eips.ethereum.org/EIPS/eip-721). + +### ERC20 + +ERC998ERC20 bottom-up composable contracts must implement the [ERC20 interface](https://eips.ethereum.org/EIPS/eip-20). ### ERC165 @@ -47,7 +55,7 @@ The [ERC165 standard](https://eips.ethereum.org/EIPS/eip-165) must be applied to ### Authentication -Authenticating whether a user or contract can execute some action works the same for both top-down and bottom-up composables. +Authenticating whether a user or contract can execute some action works the same for both ERC998ERC721 top-down and ERC998ERC721 bottom-up composables. A `rootOwner` refers to the owner address at the top of a tree of composables and ERC721 tokens. @@ -82,7 +90,7 @@ function getApproved(uint256 _tokenId) public view returns (address) { The rootOwner of a composable is gotten by calling `rootOwnerOf(uint256 _tokenId)` or `rootOwnerOfChild(address _childContract, uint256 _childTokenId)`. These functions are used by top-down and bottom-up composables to traverse up the tree of composables and ERC721 tokens to find the rootOwner. -Top-down and bottom-up composables are interoperable with each other. It is possible for a top-down composable to own a bottom-up composable or for a top-down composable to own an ERC721 token that owns a bottom-up token. In any configuration calling `rootOwnerOf(uint256 _tokenID)` on the bottom composable will return the owner address at the top of the ownership tree. +ERC998ERC721 top-down and bottom-up composables are interoperable with each other. It is possible for a top-down composable to own a bottom-up composable or for a top-down composable to own an ERC721 token that owns a bottom-up token. In any configuration calling `rootOwnerOf(uint256 _tokenID)` on a composable will return the root owner address at the top of the ownership tree. Tokens/contracts that implement the above authentication and traversal functionality are "composable aware". @@ -1053,68 +1061,93 @@ The complete interface is below. /// Note: The ERC-165 identifier for this interface is 0xffafa991 interface ERC998ERC20BottomUp { - /// @dev This emits when a token is transferred to an ERC721 token - /// @param _toContract The contract the token is transferred to - /// @param _toTokenId The token the token is transferred to - /// @param _amount The amount of tokens transferred - event TransferToParent( - address indexed _toContract, - uint256 indexed _toTokenId, - uint256 _amount - ); + /// @dev This emits when a token is transferred to an ERC721 token + /// @param _toContract The contract the token is transferred to + /// @param _toTokenId The token the token is transferred to + /// @param _amount The amount of tokens transferred + event TransferToParent( + address indexed _toContract, + uint256 indexed _toTokenId, + uint256 _amount + ); - /// @dev This emits when a token is transferred from an ERC721 token - /// @param _fromContract The contract the token is transferred from - /// @param _fromTokenId The token the token is transferred from - /// @param _amount The amount of tokens transferred - event TransferFromParent( - address indexed _fromContract, - uint256 indexed _fromTokenId, - uint256 _amount - ); + /// @dev This emits when a token is transferred from an ERC721 token + /// @param _fromContract The contract the token is transferred from + /// @param _fromTokenId The token the token is transferred from + /// @param _amount The amount of tokens transferred + event TransferFromParent( + address indexed _fromContract, + uint256 indexed _fromTokenId, + uint256 _amount + ); - /// @notice Get the balance of a non-fungible parent token - /// @param _tokenContract The contract tracking the parent token - /// @param _tokenId The ID of the parent token - /// @return amount The balance of the token - function balanceOfToken(address _tokenContract, uint256 _tokenId) - external - view - returns (uint256 amount); + /// @notice Get the balance of a non-fungible parent token + /// @param _tokenContract The contract tracking the parent token + /// @param _tokenId The ID of the parent token + /// @return amount The balance of the token + function balanceOfToken( + address _tokenContract, + uint256 _tokenId + ) + external + view + returns (uint256 amount); - /// @notice Transfer tokens from owner address to a token - /// @param _from The owner address - /// @param _toContract The ERC721 contract of the receiving token - /// @param _toToken The receiving token - /// @param _amount The amount of tokens to transfer - function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _amount) - external; + /// @notice Transfer tokens from owner address to a token + /// @param _from The owner address + /// @param _toContract The ERC721 contract of the receiving token + /// @param _toToken The receiving token + /// @param _amount The amount of tokens to transfer + function transferToParent( + address _from, + address _toContract, + uint256 _toTokenId, + uint256 _amount + ) + external; - /// @notice Transfer token from a token to an address - /// @param _fromContract The address of the owning contract - /// @param _fromTokenId The owning token - /// @param _to The address the token is transferred to - /// @param _amount The amount of tokens to transfer - function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount) - external; + /// @notice Transfer token from a token to an address + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _to The address the token is transferred to + /// @param _amount The amount of tokens to transfer + function transferFromParent( + address _fromContract, + uint256 _fromTokenId, + address _to, + uint256 _amount + ) + external; - /// @notice Transfer token from a token to an address, using ERC223 semantics - /// @param _fromContract The address of the owning contract - /// @param _fromTokenId The owning token - /// @param _to The address the token is transferred to - /// @param _amount The amount of tokens to transfer - /// @param _data Additional data with no specified format, can be used to specify the sender tokenId - function transferFromParentERC223(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount, bytes _data) - external; + /// @notice Transfer token from a token to an address, using ERC223 semantics + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _to The address the token is transferred to + /// @param _amount The amount of tokens to transfer + /// @param _data Additional data with no specified format, can be used to specify the sender tokenId + function transferFromParentERC223( + address _fromContract, + uint256 _fromTokenId, + address _to, + uint256 _amount, + bytes _data + ) + external; - /// @notice Transfer a token from a token to another token - /// @param _fromContract The address of the owning contract - /// @param _fromTokenId The owning token - /// @param _toContract The ERC721 contract of the receiving token - /// @param _toToken The receiving token - /// @param _amount The amount tokens to transfer - function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _amount) - external; + /// @notice Transfer a token from a token to another token + /// @param _fromContract The address of the owning contract + /// @param _fromTokenId The owning token + /// @param _toContract The ERC721 contract of the receiving token + /// @param _toToken The receiving token + /// @param _amount The amount tokens to transfer + function transferAsChild( + address _fromContract, + uint256 _fromTokenId, + address _toContract, + uint256 _toTokenId, + uint256 _amount + ) + external; } ``` @@ -1124,10 +1157,13 @@ interface ERC998ERC20BottomUp { /// @param _tokenContract The contract tracking the parent token /// @param _tokenId The ID of the parent token /// @return amount The balance of the token -function balanceOfToken(address _tokenContract, uint256 _tokenId) - external - view - returns (uint256 amount); +function balanceOfToken( + address _tokenContract, + uint256 _tokenId +) + external + view + returns (uint256 amount); ``` This function returns the balance of a non-fungible token. It mirrors the standard ERC20 method `balanceOf`, but accepts the address of the parent token's contract, and the parent token's ID. This method behaves identically to `balanceOf`, but checks for ownership by ERC721 tokens rather than user addresses. @@ -1139,22 +1175,31 @@ This function returns the balance of a non-fungible token. It mirrors the standa /// @param _toContract The ERC721 contract of the receiving token /// @param _toToken The receiving token /// @param _amount The amount of tokens to transfer -function transferToParent(address _from, address _toContract, uint256 _toTokenId, uint256 _amount) - external; +function transferToParent( + address _from, + address _toContract, + uint256 _toTokenId, + uint256 _amount +) + external; ``` This function transfers an amount of tokens from a user address to an ERC721 token. This function MUST ensure that the recipient contract implements ERC721 using the ERC165 `supportsInterface` function. This function SHOULD ensure that the recipient token actually exists, by calling `ownerOf` on the recipient token's contract, and ensuring it neither throws nor returns the zero address. This function MUST emit the `TransferToParent` event upon a successful transfer (in addition to the standard ERC20 `Transfer` event!). This function MUST throw if the `_from` account balance does not have enough tokens to spend. #### transferFromParent -``` -solidity +```solidity /// @notice Transfer token from a token to an address /// @param _fromContract The address of the owning contract /// @param _fromTokenId The owning token /// @param _to The address the token is transferred to /// @param _amount The amount of tokens to transfer -function transferFromParent(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount) - external; +function transferFromParent( + address _fromContract, + uint256 _fromTokenId, + address _to, + uint256 _amount +) + external; ``` This function transfers an amount of tokens from an ERC721 token to an address. This function MUST emit the `TransferFromParent` event upon a successful transfer (in addition to the standard ERC20 `Transfer` event!). This function MUST throw if the balance of the sender ERC721 token is less than the `_amount` specified. This function MUST verify that the `msg.sender` owns the sender ERC721 token, and MUST throw otherwise. @@ -1167,13 +1212,19 @@ This function transfers an amount of tokens from an ERC721 token to an address. /// @param _to The address the token is transferred to /// @param _amount The amount of tokens to transfer /// @param _data Additional data with no specified format, can be used to specify the sender tokenId -function transferFromParentERC223(address _fromContract, uint256 _fromTokenId, address _to, uint256 _amount, bytes _data) - external; +function transferFromParentERC223( + address _fromContract, + uint256 _fromTokenId, + address _to, + uint256 _amount, + bytes _data +) + external; ``` This function transfers an amount of tokens from an ERC721 token to an address. This function has identical requirements to `transferFromParent`, except that it additionally MUST invoke `tokenFallback` on the recipient address, if the address is a contract, as specified by ERC223. -### transferAsChild +#### transferAsChild ```solidity /// @notice Transfer a token from a token to another token /// @param _fromContract The address of the owning contract @@ -1181,8 +1232,14 @@ This function transfers an amount of tokens from an ERC721 token to an address. /// @param _toContract The ERC721 contract of the receiving token /// @param _toToken The receiving token /// @param _amount The amount tokens to transfer -function transferAsChild(address _fromContract, uint256 _fromTokenId, address _toContract, uint256 _toTokenId, uint256 _amount) - external; +function transferAsChild( + address _fromContract, + uint256 _fromTokenId, + address _toContract, + uint256 _toTokenId, + uint256 _amount +) + external; ``` This function transfers an amount of tokens from an ERC721 token to another ERC721 token. This function MUST emit BOTH the `TransferFromParent` and `TransferToParent` events (in addition to the standard ERC20 `Transfer` event!). This function MUST throw if the balance of the sender ERC721 token is less than the `_amount` specified. This function MUST verify that the `msg.sender` owns the sender ERC721 token, and MUST throw otherwise. This function MUST ensure that the recipient contract implements ERC721 using the ERC165 `supportsInterface` function. This function SHOULD ensure that the recipient token actually exists, by calling `ownerOf` on the recipient token's contract, and ensuring it neither throws nor returns the zero address. From fda347d0f454d3eb7a9cf49c96a4e391fe3e1cc5 Mon Sep 17 00:00:00 2001 From: Philippe Castonguay Date: Thu, 2 Aug 2018 10:58:54 -0400 Subject: [PATCH 084/177] ERC-1271 : Standard Signature Validation Method for Contracts (#1258) * initial commit * Update table * Update EIP name & number in table * Change EIP name + discussion link * Edit authors --- EIPS/eip-1271.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 EIPS/eip-1271.md diff --git a/EIPS/eip-1271.md b/EIPS/eip-1271.md new file mode 100644 index 00000000..95546d33 --- /dev/null +++ b/EIPS/eip-1271.md @@ -0,0 +1,82 @@ +--- +eip: 1271 +title: Standard Signature Validation Method for Contracts +author: Francisco Giordano (@frangio), Matt Condon (@shrugs), Philippe Castonguay (@PhABC) +discussions-to: https://github.com/ethereum/EIPs/issues/1271 +status: Draft +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. + +## 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 act as an account, which can include identity based methods (e.g. [ERC-725](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-725.md) & [ERC-1078](https://github.com/alexvandesande/EIPs/blob/ee2347027e94b93708939f2e448447d030ca2d76/EIPS/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. + +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). + + + +## 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). + +```javascript +/** +* @dev Should return whether the signature provided is valid for the provided data +* @param _data Arbitrary length data signed on the behalf of address(this) +* @param _signature Signature byte array associated with _data +* +* MUST return a bool upon valid or invalid signature with corresponding _data +* MUST take (bytes, bytes) as arguments +*/ +function isValidSignature( + bytes _data, + bytes _signature) + public + view + returns (bool isValid); +``` + +`isValidSignature` can call arbitrary methods to validate a given signature, which could be context dependent (e.g. time based or state based), EOA dependant (e.g. signers authorization level within smart account), signature scheme Dependant (e.g. ECDSA, multisig, BLS), etc. + + + +## Rationale + + +Such a function is important because it allows *other contracts* to validate signed messages on the behalf of the smart account. This is necessary because not all signed messages will first pass by the smart account 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 Account A -> Contract C` (e.g. owner of smart account `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 account `A` to transfer tokens `T` when event `E` is triggered). + +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 account. Hence, an signed action message is only valid when the signer is authorized to perform a given action on the behalf of a smart account. + +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. + +## 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. + +## Implementation + + +Existing implementations : + +* 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/). From 0580dea7dcac5e785e81a124b29d43da66ef4de9 Mon Sep 17 00:00:00 2001 From: Zainan Victor Zhou Date: Thu, 2 Aug 2018 08:13:02 -0700 Subject: [PATCH 085/177] Update EIP-1 to fix `discussion-to` and `resolution` URL field (#1281) * Add notion that Github PR can't be discussion-to * Update eip-1.md * Update it is interpret as HTML tag * Update eip-1.md * Update eip-1.md * Update eip-1.md --- EIPS/eip-1.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md index 62501b03..5fb2a667 100644 --- a/EIPS/eip-1.md +++ b/EIPS/eip-1.md @@ -99,7 +99,9 @@ Each EIP must begin with an RFC 822 style header preamble, preceded and followed ` author:` -` * discussions-to:` +` * discussions-to:` \ + + - :x: `discussions-to` can not be a Github `Pull-Request`. ` status:` @@ -117,7 +119,7 @@ Each EIP must begin with an RFC 822 style header preamble, preceded and followed ` * superseded-by:` -` * resolution:` +` * resolution:` \ #### Author header From b0213749e914adee20459c1ec7a17d444685f5dd Mon Sep 17 00:00:00 2001 From: benk10 Date: Fri, 3 Aug 2018 14:01:45 +0300 Subject: [PATCH 086/177] EIP 1285: Increase Gcallstipend gas in the CALL OPCODE (#1286) * Added first draft for EIP1285 * Fixed markdown * Added discussions-to link --- EIPS/eip-1285.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 EIPS/eip-1285.md diff --git a/EIPS/eip-1285.md b/EIPS/eip-1285.md new file mode 100644 index 00000000..8416cf6e --- /dev/null +++ b/EIPS/eip-1285.md @@ -0,0 +1,41 @@ +--- +eip: 1285 +title: Increase Gcallstipend gas in the CALL OPCODE +author: Ben Kaufman +discussions-to: https://ethereum-magicians.org/t/eip-1285-increase-gcallstipend-gas-in-the-call-opcode/941 +status: Draft +type: Standards Track +category: Core +created: 2018-08-01 +--- + +## Simple Summary + +Increase the ``Gcallstipend`` fee parameter in the ``CALL`` OPCODE from ``2,300`` to ``3,500`` gas units. + +## Abstract + +Currently, the ``CALL`` OPCODE forwards a stipend of ``2,300`` gas units for a non zero value ``CALL`` operations where a contract is called. This stipend is given to the contract to allow execution of its ``fallback`` function. The stipend given is intentionally small in order to prevent the called contract from spending the call gas or performing an attack (like re-entrancy). +While the stipend is small it should still give the sufficient gas required for some cheap OPCODES like ``LOG``, but it's not enough for some more complex and modern logics to be implemented. +This EIP proposes to increase the given stipend from ``2,300`` to ``3,500`` to increase the usability of the ``fallback`` function. + + +## Motivation + +The main motivation behind this EIP is to allow simple fallback functions to be implemented for contracts following the ``"Proxy"`` pattern. Simply explained, a ``"Proxy Contract"`` is a contract which use ``DELEGATECALL`` in its ``fallback`` function to behave according to the logic of another contract and serve as an independent instance for the logic of the contract it points to. +This pattern is very useful for saving gas per deployment (as Proxy contracts are very lean) and it opens the ability to experiment with upgradability of contracts. +On average, the ``DELEGATECALL`` functionality of a proxy contract costs about 1,000 gas units. +When a contract transfers ETH to a proxy contract, the proxy logic will consume about 1,000 gas units before the ``fallback`` function of the logic contract will be executed. This leaves merely about 1,300 gas units for the execution of the logic. This is a severe limitation as it is not enough for an average ``LOG`` operation (it might be enough for a ``LOG`` with one parameter). +By slightly increasing the gas units given in the stipend we allow proxy contracts have proper ``fallback`` logic without increasing the attack surface of the calling contract. + +## Specification + +Increase the ``Gcallstipend`` fee parameter in the ``CALL`` OPCODE from ``2,300`` to ``3,500`` gas units (further specification will be provided later). + +## Rationale + +The rational for increasing the ``Gcallstipend`` gas parameter by ``1,200`` gas units comes from the cost of performing ``DELEGATECALL`` and ``SLOAD`` with a small margin for some small additional operations. All while still keeping the stipend relatively small. + +## Backwards Compatibility + +This EIP requires a backwards incompatible change for the ``Gcallstipend`` gas parameter in the ``CALL`` OPCODE. From 18ea1fc9a40cde083102cf6cd47e774fb9d61ee5 Mon Sep 17 00:00:00 2001 From: yarrumretep Date: Fri, 3 Aug 2018 07:03:20 -0400 Subject: [PATCH 087/177] Moving EIP-1167 to Last Call status (#1292) --- EIPS/eip-1167.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index d226cfef..d664f8f2 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -3,7 +3,7 @@ eip: 1167 title: Minimal Proxy Contract author: Peter Murray (@yarrumretep), Nate Welch (@flygoing), Joe Messerman (@JAMesserman) discussions-to: https://github.com/optionality/clone-factory/issues/10 -status: Draft +status: Last Call type: Standards Track category: ERC created: 2018-06-22 From c7b1056d00da867f0a4c29201dfdec877c8f3069 Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Fri, 3 Aug 2018 04:15:54 -0700 Subject: [PATCH 088/177] Automatically merged updates to draft EIP(s) 1193 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1193.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md index c60a99d9..afbe3930 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -298,7 +298,7 @@ class EthereumProvider extends EventEmitter { const id = this._nextJsonrpcId++; const jsonrpc = '2.0'; - const payload = { jsonrpc, id method, params }; + const payload = { jsonrpc, id, method, params }; const promise = new Promise((resolve, reject) => { this._promises[payload.id] = { resolve, reject }; From 268b14102e818ec8c4d063bbb964c1df7e792198 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 3 Aug 2018 12:49:19 +0100 Subject: [PATCH 089/177] Fix capitalisation of "standards track" (#1293) * Fix capitalisation of "standards track" * Fix capitalisation of "standards track" --- EIPS/eip-926.md | 2 +- EIPS/eip-927.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-926.md b/EIPS/eip-926.md index 3d54e8af..5e210373 100644 --- a/EIPS/eip-926.md +++ b/EIPS/eip-926.md @@ -2,7 +2,7 @@ eip: 926 title: Address metadata registry author: Nick Johnson -type: Standards track +type: Standards Track category: ERC status: Draft created: 2018-03-12 diff --git a/EIPS/eip-927.md b/EIPS/eip-927.md index a207d0e7..4087813b 100644 --- a/EIPS/eip-927.md +++ b/EIPS/eip-927.md @@ -2,7 +2,7 @@ eip: 927 title: Generalised authorisations author: Nick Johnson -type: Standards track +type: Standards Track category: ERC status: Draft created: 2018-03-12 From a7b2fbe54db46b4d133ceed7ec5467a430f16e71 Mon Sep 17 00:00:00 2001 From: The Officious BokkyPooBah Date: Mon, 6 Aug 2018 22:02:34 +1000 Subject: [PATCH 090/177] Automatically merged updates to draft EIP(s) 1167 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1167.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index d664f8f2..29b04692 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -222,6 +222,6 @@ contract ContractProbe { } } ``` -The ContractProbe contract is deployed on Kovan at `0x8b98e65e0e8bce0f71a2a22f3d2666591e4cc857` and on Mainnet at `0x0c953133aa046965b83a3de1215ed4285414537c` +The ContractProbe contract is deployed on Kovan at `0x8b98e65e0e8bce0f71a2a22f3d2666591e4cc857`, Ropsten at `0x75f09888af7c9bdfe15317c411dfb03636179a6d` and on Mainnet at `0x0c953133aa046965b83a3de1215ed4285414537c` ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From ab69af00ea359f05fd010e37f93b377143baaaa4 Mon Sep 17 00:00:00 2001 From: atlanticcrypto <32847326+atlanticcrypto@users.noreply.github.com> Date: Mon, 6 Aug 2018 11:22:58 -0400 Subject: [PATCH 091/177] Modify Proof of Work Incentive Structure and Remove Difficulty Bomb (#1295) * Initial commit. * Update eip-modify-pow-incentive.md * Update eip-modify-pow-incentive.md * Update eip-modify-pow-incentive.md * Update eip-modify-pow-incentive.md * Update eip-modify-pow-incentive.md * Update eip-modify-pow-incentive.md * Create eip-X.md * Create eip-modify-pow-incentive.md * Delete eip-modify-pow-incentive.md * Update and rename eip-modify-pow-incentive.md to eip-1295.md * Update eip-1295.md --- EIPS/eip-1295.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 EIPS/eip-1295.md diff --git a/EIPS/eip-1295.md b/EIPS/eip-1295.md new file mode 100644 index 00000000..8a6345a2 --- /dev/null +++ b/EIPS/eip-1295.md @@ -0,0 +1,83 @@ +--- +eip: 1295 +title: Modify Ethereum PoW Incentive Structure and Diffuse Difficulty Bomb +author: +discussions-to: https://github.com/atlanticcrypto/Discussion/issues/1 +status: Draft +type: Standards Track +category: Core +created: 2018-08-05 +--- + + +## Simple Summary + +Network security and overall ecosystem maturity warrants the continued incentivization of Proof of Work participation but allows for a reduction in ancillary ETH issuance and the removal of the Difficulty Bomb. This EIP proposes a reduction of Uncle and removal of Nephew rewards while diffusing and removing the Difficulty Bomb with the Constantinople hard fork. + +## Abstract + +Starting with CNSTNTNPL_FORK_BLKNUM the client will calculate the difficulty without the additional exponential component. Furthermore, Uncle rewards will be adjusted and Nephew rewards will be removed to eliminate excess ancillary ETH issuance. The current ETH block reward of 3 ETH will remain constant. + + +## Motivation + +Network scalability and security are at the forefront of risks to the Ethereum protocol. With great strides being made towards on and off chain scalability, the existence of an artificial throughput limiting device in the protocol is no longer warranted. Removing the risk of reducing throughput through the initialization of the Difficulty Bomb is "low-hanging-fruit" to ensure continued operation at a minimum of current throughput into the future. + +The security layer of the Ethereum network is and should remain robust. Incentives for continued investment into the security of the growing ecosystem are paramount. At the same time, the ancillary issuance benefits of the Ethereum protocol can be adjusted to reduce the overall issuance profile. Aggressively adjusting Uncle and removing Nephew rewards will reduce the inflationary aspect of ETH issuance, while keeping the current block reward constant at 3 ETH will ensure top line incentives stay in place. + + +## Specification + +#### Remove Difficulty Bomb +For the purposes of calc_difficulty, simply remove the exponential difficulty adjustment component, epsilon, i.e. the int(2**((block.number // 100000) - 2)). + +#### Adjust Uncle and Nephew rewards +If an uncle is included in a block for `block.number >= CNSTNTNPL_FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (3 - k) * new_block_reward / 8 + +This is the existing pre-Constantinople formula for uncle rewards, adjusted to reward 2 levels of Uncles at a reduced rate. + +The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is + + new_nephew_reward = 0 + +This is a removal of all rewards for Nephew blocks. + + +## Rationale + +The Difficulty Bomb was instituted as a type of planned obsolescence to force the implementation of network upgrades with regular frequency. With the success of and resulting investment in the Ethereum protocol, these network upgrades have been secured through outright adoption and the size of the development community. With a result of the Difficulty Bomb being a reduction in effective network throughput and the risk of inaction inside the development community being reduced substantially, the original purpose of the Difficulty Bomb seems to have been diffused itself. With the focus on scalability for the protocol, eliminating a mechanism whereby the throughput of the network can be limited artificially, due to any circumstance, is a logical step to ensure continued minimum network operation at the current throughput level. + +Through August 4th, the 2018 year to date reward issued to Uncle blocks totaled over 635,000 ETH. The average reward per Uncle totals 2.27 ETH. The year to date average Uncle rate is 22.49%. Using the year to date metrics, the ongoing average ETH paid as an Uncle reward per block is 0.51 ETH plus the Uncle inclusion reward of 0.021 ETH (0.09375 ETH * .2249), total Uncle related reward per block being over 0.53 ETH. With total year to date block rewards totaling approximately 3,730,000 ETH, the network has paid an additional 17pct in Uncle rewards. This is issuance that can be reduced while still maintaining the overall integrity and incentivization of network security. + +Reducing the issuance of ETH in rewarding Uncle blocks (forked blocks caused by latency in propagation, a multi-faceted problem) should directly incentivize the investment in technologies and efficiencies to reduce the overall network Uncle rate which may lead to a reduction in Network wide Uncle rates, reducing ancillary issuance further. + +Reducing the Uncle rewards from the current specification to the proposed will yield two levels of ancillary ETH issuance for Uncles: + +Level 1 Uncle -> 0.75 ETH +Level 2 Uncle -> 0.375 ETH + +These levels are sufficient to continue incentivizing decentralized participation, while also providing an immediate economic incentive for the upgrade of the Ethereum node network and its tangential infrastructure. + +The Nephew reward structure should be entirely eliminated. + +With a reduction to the Uncle and removal of the Nephew reward, ancillary ETH issuance should drop (under normal market conditions, i.e. 22.49% uncle rate) by over 75pct and total ETH issuance from the successful sealing and mining of valid blocks should drop over 10pct. + +Paired with the diffusal and removal of the Difficulty Bomb, this proposal strikes a balance between ensuring status quo network throughput, reducing overall ETH issuance, and continuing top line incentives for investment in infrastructure and efficiencies to continue expanding the Ethereum protocol's operational security. + + +## Backwards Compatibility + +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, Constantinople. + +## Test Cases + +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementation + +Forthcoming. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 1b8ebf86fdfe884337a0b66127878791d113403e Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Mon, 6 Aug 2018 11:29:36 -0400 Subject: [PATCH 092/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 62c8adde..ac823f63 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -47,7 +47,7 @@ IF non-Ethereum environment #### `[1] REQUEST` -Dapps MUST request an Ethereum provider API by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of ETHEREUM_PROVIDER_REQUEST and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK". +Dapps MUST request an Ethereum provider API by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of "ETHEREUM_PROVIDER_REQUEST" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK". #### `[2] INJECT` @@ -55,7 +55,7 @@ Ethereum-enabled DOM environments MUST expose an Ethereum provider API as a glob #### `[3] NOTIFY` -Ethereum-enabled DOM environments MUST notify dapps of successful provider API exposure by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of ETHEREUM_PROVIDER_SUCCESS" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK" +Ethereum-enabled DOM environments MUST notify dapps of successful provider API exposure by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of "ETHEREUM_PROVIDER_SUCCESS" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK" #### `[4] NOOP` @@ -66,17 +66,17 @@ If a user rejects access to the Ethereum provider API on an untrusted site, the The following example demonstrates one possible implementation of this strategy in a browser-based DOM environment. Note that Ethereum-enabled environments on other platforms would most likely use platform-specific native messaging protocols, not `postMessage`. ```js -// Listen for provider API exposure -window.addEventListener('message', function (event) { - if (!event.data || !event.data.type) { return; } - if (event.data.type === 'ETHEREUM_PROVIDER_SUCCESS') { - // Provider API exposed, continue - const networkVersion = await ethereum.send('net_version', []); - console.log(networkVersion); - } +window.addEventListener('load', () => { + // Listen for provider injection + window.addEventListener('message', ({ data }) => { + if (data && data.type && data.type === 'ETHEREUM_PROVIDER_SUCCESS') { + // Provider API exposed, continue + const networkVersion = await ethereum.send('net_version', []); + } + }); + // Request provider + window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, '*'); }); -// Request Provider API -window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }); ``` ## Rationale From 4f8f1cbd1d46a0b9fa0a6313aa6b91efe37fc948 Mon Sep 17 00:00:00 2001 From: Micah Zoltu Date: Tue, 7 Aug 2018 17:31:44 +0800 Subject: [PATCH 093/177] Changes EIP-1 wording to focus on technicals not community sentiment. (#1224) * Changes EIP-1 wording to focus on technicals not community sentiment. All of the recent changes to the EIP process have been made to ensure that the EIP process is a technical one, and not one of sentiment analysis. There is a lot of discussion going on with regards to how we can improve the process and get valid community sentiment analysis pre-fork, but there doesn't exist a complete solution yet at this time (just proposals). It appears that the Last Call PR introduced sentiment analysis into the process, which I do not believe was intended. From my recollection of the discussions around the Last Call stuff, the goal wasn't to fundamentally change how governance works, but rather to ensure that EIPs don't get stuck indefinitely in limbo. This change simply removes the sentiment analysis wording from the process and makes it more clear that the EIP process is about gauging technical feasibility, not making judgement calls as to whether or not a thing is a good idea or not. * Adds Active state --- EIPS/eip-1.md | 17 +++++++++-------- README.md | 8 +++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md index 5fb2a667..01717cc6 100644 --- a/EIPS/eip-1.md +++ b/EIPS/eip-1.md @@ -14,7 +14,7 @@ EIP stands for Ethereum Improvement Proposal. An EIP is a design document provid ## EIP Rationale -We intend EIPs to be the primary mechanisms for proposing new features, for collecting community input on an issue, and for documenting the design decisions that have gone into Ethereum. Because the EIPs are maintained as text files in a versioned repository, their revision history is the historical record of the feature proposal. +We intend EIPs to be the primary mechanisms for proposing new features, for collecting community technical input on an issue, and for documenting the design decisions that have gone into Ethereum. Because the EIPs are maintained as text files in a versioned repository, their revision history is the historical record of the feature proposal. For Ethereum implementers, EIPs are a convenient way to track the progress of their implementation. Ideally each implementation maintainer would list the EIPs that they have implemented. This will give end users a convenient way to know the current status of a given implementation or library. @@ -48,24 +48,25 @@ Your role as the champion is to write the EIP using the style and format describ Each status change is requested by the EIP author and reviewed by the EIP editors. Use a pull request to update the status. Please include a link to where people should continue discussing your EIP. The EIP editors will process these requests as per the conditions below. +* **Active** -- Some Informational and Process EIPs may also have a status of “Active” if they are never meant to be completed. E.g. EIP 1 (this EIP). * **Work in progress (WIP)** -- Once the champion has asked the Ethereum community whether an idea has any chance of support, they will write a draft EIP as a [pull request]. Consider including an implementation if this will aid people in studying the EIP. * :arrow_right: Draft -- If agreeable, EIP editor will assign the EIP a number (generally the issue or PR number related to the EIP) and merge your pull request. The EIP editor will not unreasonably deny an EIP. * :x: Draft -- Reasons for denying draft status include being too unfocused, too broad, duplication of effort, being technically unsound, not providing proper motivation or addressing backwards compatibility, or not in keeping with the [Ethereum philosophy](https://github.com/ethereum/wiki/wiki/White-Paper#philosophy). * **Draft** -- Once the first draft has been merged, you may submit follow-up pull requests with further changes to your draft until such point as you believe the EIP to be mature and ready to proceed to the next status. An EIP in draft status must be implemented to be considered for promotion to the next status (ignore this requirement for core EIPs). * :arrow_right: Last Call -- If agreeable, the EIP editor will assign Last Call status and set a review end date, normally 14 days later. - * :x: Last Call -- A request for Last Call status will be denied if material changes are still expected to be made to the draft. We hope that EIPs only enter Last Call once, so as to avoid unnecessary noise on the RSS feed. Last Call will be denied if the implementation is not complete and supported by the community. + * :x: Last Call -- A request for Last Call status will be denied if material changes are still expected to be made to the draft. We hope that EIPs only enter Last Call once, so as to avoid unnecessary noise on the RSS feed. * **Last Call** -- This EIP will listed prominently on the http://eips.ethereum.org/ website (subscribe via RSS at [last-call.xml](/last-call.xml)). - * :x: -- A Last Call which results in material changes or substantial unaddressed complaints will cause the EIP to revert to Draft. - * :arrow_right: Accepted (Core EIPs only) -- After the review end date, the Ethereum Core Developers will vote on whether to accept this change. If yes, the status will upgrade to Accepted. - * :arrow_right: Final (Not core EIPs) -- A successful Last Call without material changes or unaddressed complaints will become Final. -* **Accepted (Core EIPs only)** -- This is being implemented by Ethereum Core Developers. - * :arrow_right: Final -- Standards Track Core EIPs must be implemented in at least three viable Ethereum clients before it can be considered Final. When the implementation is complete and supported by the community, the status will be changed to “Final”. + * :x: -- A Last Call which results in material changes or substantial unaddressed technical complaints will cause the EIP to revert to Draft. + * :arrow_right: Accepted (Core EIPs only) -- A successful Last Call without material changes or unaddressed technical complaints will become Accepted. + * :arrow_right: Final (Not core EIPs) -- A successful Last Call without material changes or unaddressed technical complaints will become Final. +* **Accepted (Core EIPs only)** -- This EIP is in the hands of the Ethereum client developers. Their process for deciding whether to encode it into their clients as part of a hard fork is not part of the EIP process. + * :arrow_right: Final -- Standards Track Core EIPs must be implemented in at least three viable Ethereum clients before it can be considered Final. When the implementation is complete and adopted by the community, the status will be changed to “Final”. * **Final** -- This EIP represents the current state-of-the-art. A Final EIP should only be updated to correct errata. Other exceptional statuses include: * Deferred -- This is for core EIPs that have been put off for a future hard fork. -* Rejected -- An EIP that is fundamentally broken and will not be implemented. +* Rejected -- An EIP that is fundamentally broken or a Core EIP that was rejected by the Core Devs and will not be implemented. * Active -- This is similar to Final, but denotes an EIP which which may be updated without changing its EIP number. * Superseded -- An EIP which was previously final but is no longer considered state-of-the-art. Another EIP will be in Final status and reference the Superseded EIP. diff --git a/README.md b/README.md index 723838b9..ad7b3575 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,11 @@ When you believe your EIP is mature and ready to progress past the draft phase, - **For all other EIPs**, open a PR changing the state of your EIP to 'Final'. An editor will review your draft and ask if anyone objects to its being finalised. If the editor decides there is no rough consensus - for instance, because contributors point out significant issues with the EIP - they may close the PR and request that you fix the issues in the draft before trying again. # EIP Status Terms -* **Draft** - an EIP that is open for consideration. -* **Accepted** - an EIP that is planned for immediate adoption, i.e. expected to be included in the next hard fork (for Core/Consensus layer EIPs). -* **Final** - an EIP that has been adopted in a previous hard fork (for Core/Consensus layer EIPs). +* **Draft** - an EIP that is undergoing rapid iteration and changes +* **Last Call** - an EIP that is done with its initial iteration and ready for review by a wide audience +* **Accepted** - a core EIP that has been in Last Call for at least 2 weeks and any technical changes that were requested have been addressed by the author +* **Final (non-Core)** - an EIP that has been in Last Call for at least 2 weeks and any technical changes that were requested have been addressed by the author. +* **Final (Core)** - an EIP that the Core Devs have decide to implement and release in a future hard fork or has already been released in a hard fork * **Deferred** - an EIP that is not being considered for immediate adoption. May be reconsidered in the future for a subsequent hard fork. # Preferred Citation Format From d087ea8910f1768b325187ae73dea068ae2c2838 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Tue, 7 Aug 2018 19:02:55 +0800 Subject: [PATCH 094/177] EIP-1283: Net gas metering for SSTORE without dirty maps (#1283) * Net gas metering for SSTORE without dirty maps * typo: opcode * typo: changed * Self-assign the PR number 1283 * Add a dummy discussion url * Fix R_sclear loopholes * Properly handle refund for 0 value issue * fix: refund should only be added again if new value is 0 * clarify if statement * Clearly state what () means * typo fix: unnecessary wording "additional" * fix: should have parent clause if original value is not zero * Remove 15k gas from refund counter instead of deduct it as gas cost * Be more clear on EIP-658 enabled only-commit-storage-changes-at-end-of-block optimizations * Move some discussion comments to motivations section * typo: commons -> common * Be more specific when gas reduction won't happen compared with EIP-1087 * typo: duplicate description * Add explanation section * becomes -> become * typo: covers -> cover * Add state transition diagrams * Fix table formatting * typo: 0 -> `current` * typo: missing - * Change state transition table to use `(current, original)` vs `new` * fix: vertical <-> horizontal * Be more specific on usages benefited by this EIP * Typo fix --- EIPS/eip-1283.md | 226 ++++++++++++++++++++++++++++++++++++++ assets/eip-1283/state.png | Bin 0 -> 176309 bytes 2 files changed, 226 insertions(+) create mode 100644 EIPS/eip-1283.md create mode 100644 assets/eip-1283/state.png diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md new file mode 100644 index 00000000..40bd258c --- /dev/null +++ b/EIPS/eip-1283.md @@ -0,0 +1,226 @@ +--- +eip: 1283 +title: Net gas metering for SSTORE without dirty maps +author: Wei Tang (@sorpaas) +discussions-to: https://github.com/sorpaas/EIPs/issues/1 +status: Draft +type: Standards Track +category: Core +created: 2018-08-01 +--- + +## Abstract + +This EIP proposes net gas metering changes for SSTORE opcode, as an +alternative for EIP-1087. It tries to be friendlier to implementations +that uses different opetimiazation strategies for storage change +caches. + +## Motivation + +EIP-1087 proposes a way to adjust gas metering for SSTORE opcode, +enabling new usages on this opcodes where it is previously too +expensive. However, EIP-1087 requires keeping a dirty map for storage +changes, and implictly makes the assumption that a transaction's +storage changes are committed to the storage trie at the end of a +transaction. This works well for some implementations, but not for +others. After EIP-658, some implementations do the optimization to +only commit storage changes at the end of a block. For them, it is +possible to know a storage's original value and current value, but it +is not possible to iterate over all storage changes. For EIP-1087, +they will need to keep a separate dirty map to keep track of gas +costs. This adds additional memory consumptions. + +This EIP proposes an alternative way for gas metering on SSTORE, using +information that is more universially available to most +implementations: + +* *Storage slot's original value*. This is the value of the storage if + a call/create reversion happens on the current VM execution + context. It is universially available because all clients need to + keep track of call/create reversion. +* *Storage slot's current value*. +* Refund counter. + +This EIP indeed has edge cases where it may consume more gases +compared with EIP-1087 (see Rationale), but it can be worth the trade +off: + +* We don't suffer from the optimization limitation of EIP-1087. After + EIP-658, an efficient storage cache implementation would probably + use an in-memory trie (without RLP encoding/decoding) or other + immutable data structures to keep track of storage changes, and only + commit changes at the end of a block. For those implementations, we + cannot efficiently iterate over a transaction's storage change slots + without doing a full diff of the trie. +* It never costs more gases compared with current scheme. +* It covers most common usages. + +Usages that benefits from this EIP's gas reduction scheme includes: + +* Subsequent storage write operations within the same call frame. This + includes reentry locks, same-contract multi-send, etc. +* Passing storage information from sub call frame to parent call + frame, where this information does not need to be persistent outside + of a transaction. This includes sub-frame error codes and message + passing, etc. + +## Specification + +Term *original value* is as defined in Motivation. *Current value* +refers to the storage slot value before SSTORE happens. *New value* +refers to the storage slot value after SSTORE happens. + +Replace SSTORE opcode gas cost calculation (including refunds) with +the following logic: + +* If *current value* equals *new value* (this is a no-op), 200 gas is + deducted. +* If *current value* does not equal *new value* + * If *original value* equals *current value* (this storage slot has + not been changed by the current execution context) + * If *original value* is 0, 20000 gas is deducted. + * Otherwise, 5000 gas is deducted. If *new value* is 0, add 15000 + gas to refund counter. + * If *original value* does not equal *current value* (this storage + slot is dirty), 200 gas is deducted. Apply both of the following + clauses. + * If *original value* is not 0 + * If *current value* is 0 (also means that *new value* is not + 0), remove 15000 gas from refund counter. We can prove that + refund counter will never go below 0. + * If *new value* is 0 (also means that *current value* is not + 0), add 15000 gas to refund counter. + * If *original value* equals *new value* (this storage slot is + reset) + * If *original value* is 0, add 19800 gas to refund counter. + * Otherwise, add 4800 gas to refund counter. + +Refund counter works as before -- it is limited to half of the gas +consumed. + +## Explanation + +The new gas cost scheme for SSTORE is divided to three different +types: + +* **No-op**: the virtual machine does not need to do anything. This is + the case if *current value* equals *new value*. +* **Fresh**: this storage slot has not been changed, or has been reset + to its original value either on current frame, or on a sub-call + frame for the same contract. This is the case if *current value* + does not equal *new value*, and *original value* equals *current + value*. +* **Dirty**: this storage slot has already been changed, either on + current frame or on a sub-call frame for the same contract. This is + the case if *current value* does not equal *new value*, and + *original value* does not equal *current value*. + +We can see that the above three types cover all possible variations of +*original value*, *current value*, and *new value*. + +**No-op** is a trivial operation. Below we only consider cases for +**Fresh** and **Dirty**. + +All initial (not-**No-op**) SSTORE on a particular storage slot starts +with **Fresh**. After that, it will become **Dirty** if the value has +been changed (either on current call frame or a sub-call frame for the +same contract). When going from **Fresh** to **Dirty**, we charge the +gas cost the same as current scheme. + +When entering a sub-call frame, a previously-marked **Dirty** storage +slot will again become **Fresh**, but only for this sub-call +frame. Note that we don't charge any more gases compared with current +scheme in this case. + +In current call frame, a **Dirty** storage slot can be reset back to +**Fresh** via a SSTORE opcode either on current call frame or a +sub-call frame. For current call frame, this dirtiness is tracked, so +we can issue refunds. For sub-call frame, it is not possible to track +this dirtiness reset, so the refunds (for *current call frame*'s +initial SSTORE from **Fresh** to **Dirty**) are not issued. In the +case where refunds are not issued, the gas cost is the same as the +current scheme. + +When a storage slot remains at **Dirty**, we charge 200 gas. In this +case, we would also need to keep track of `R_SCLEAR` refunds -- if we +already issued the refund but it no longer applies (*current value* is +0), then removes this refund from the refund counter. If we didn't +issue the refund but it applies now (*new value* is 0), then adds this +refund to the refund counter. It is not possible where a refund is not +issued but we remove the refund in the above case, because all storage +slot starts with **Fresh** state, either on current call frame or a +sub-call frame. + +### State Transition + +Below is a graph ([by +@Arachnid](https://github.com/ethereum/EIPs/pull/1283#issuecomment-410229053)) +showing possible state transition of gas costs. Note that this applies +to current call frame only, and we ignore **No-op** state because that +is trivial: + +![State Transition](../assets/eip-1283/state.png) + +Below are table version of the above diagram. Vertical shows the *new +value* being set, and horizontal shows the state of *original value* +and *current value*. + +When *original value* is 0: + +| | A (`current=orig=0`) | B (`current!=orig`) | +|----|----------------------|--------------------------| +| ~0 | B; 20k gas | B; 200 gas | +| 0 | A; 200 gas | A; 200 gas, 19.8k refund | + +When *original value* is not 0: + +| | X (`current=orig!=0`) | Y (`current!=orig`) | Z (`current=0`) | +|-------------|-----------------------|-------------------------|---------------------------| +| `orig` | X; 200 gas | X; 200 gas, 4.8k refund | X; 200 gas, -10.2k refund | +| `~orig, ~0` | Y; 5k gas | Y; 200 gas | Y; 200 gas, -15k refund | +| 0 | Z; 5k gas, 15k refund | Z; 200 gas, 15k refund | Z; 200 gas | + +## Rationale + +This EIP mostly archives what EIP-1087 tries to do, but without the +complexity of introducing the concept of "dirty maps". One limitation +is that for some edge cases dirtiness will not be tracked: + +* The first SSTORE for a storage slot on a sub-call frame for the same + contract won't benefit from gas reduction. +* If a storage slot is changed, and it's reset to its original + value. The next SSTORE to the same storage slot won't benefit from + gas reduction. + +Examine examples provided in EIP-1087's Motivation: + +* If a contract with empty storage sets slot 0 to 1, then back to 0, + it will be charged `20000 + 200 - 19800 = 400` gas. +* A contract with empty storage that increments slot 0 5 times will be + charged `20000 + 5 * 200 = 21000` gas. +* A balance transfer from account A to account B followed by a + transfer from B to C, with all accounts having nonzero starting and + ending balances + * If the token contract has multi-send function, it will cost + `5000 * 3 + 200 - 4800 = 10400` gas. + * If this transfer from A to B to C is invoked by a third-party + contract, and the token contract has no multi-send function, then + it won't benefit from this EIP's gas reduction. + +## Backwards Compatibility + +This EIP requires a hard fork to implement. No gas cost increase is +anticipated, and many contract will see gas reduction. + +## Test Cases + +To be added. + +## Implementation + +To be added. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/assets/eip-1283/state.png b/assets/eip-1283/state.png new file mode 100644 index 0000000000000000000000000000000000000000..1737d2578630a83aa49ad6eb13c9a2e2f044f632 GIT binary patch literal 176309 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

    (;ezxmXRmVi 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%0D(*DQ-I>Z0*e&Fs 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

    3a=L&SXWVYtcwR*Mzp2iy10Hs48O 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

    tR*Q>seAQB0KG=VoiJP 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 Date: Tue, 7 Aug 2018 08:25:57 -0700 Subject: [PATCH 095/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index 2b5c5350..25491748 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -15,7 +15,6 @@ An extension of the [ERC721 standard](https://eips.ethereum.org/EIPS/eip-721) to An extension of the [ERC20](https://eips.ethereum.org/EIPS/eip-20) and [ERC223](https://github.com/ethereum/EIPs/issues/223) standards to enable ERC20 and ERC223 tokens to be owned by ERC721 tokens. - This specification covers four different kinds of composable tokens: 1. [ERC998ERC721 top-down composable tokens that receive, hold and transfer ERC721 tokens](#erc721-top-down-composable) @@ -23,7 +22,6 @@ This specification covers four different kinds of composable tokens: 3. [ERC998ERC721 bottom-up composable tokens that attach themselves to other ERC721 tokens.](#erc721-bottom-up-composable) 4. [ERC998ERC20 bottom-up composable tokens that attach themselves to ERC721 tokens.](#erc20-bottom-up-composable) - ## Abstract 1. An ERC988ERC721 top-down composable is an ERC721 token with additional functionality for owning other ERC721 tokens. @@ -146,19 +144,19 @@ interface ERC998ERC721TopDown { /// @dev This emits when a token receives a child token. /// @param _from The prior owner of the token. - /// @param _tokenId The token that receives the child token. + /// @param _toTokenId The token that receives the child token. event ReceivedChild( address indexed _from, - uint256 indexed _tokenId, + uint256 indexed _toTokenId, address indexed _childContract, uint256 _childTokenId ); /// @dev This emits when a child token is transferred from a token to an address. - /// @param _tokenId The parent token that the child token is being transferred from. + /// @param _fromTokenId The parent token that the child token is being transferred from. /// @param _to The new owner address of the child token. event TransferChild( - uint256 indexed tokenId, + uint256 indexed _fromTokenId, address indexed _to, address indexed _childContract, uint256 _childTokenId @@ -212,10 +210,12 @@ interface ERC998ERC721TopDown { returns(bytes4); /// @notice Transfer child token from top-down composable to address. + /// @param _fromTokenId The owning token to transfer from. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. function transferChild( + uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId @@ -223,10 +223,12 @@ interface ERC998ERC721TopDown { external; /// @notice Transfer child token from top-down composable to address. + /// @param _fromTokenId The owning token to transfer from. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. function safeTransferChild( + uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId @@ -234,11 +236,13 @@ interface ERC998ERC721TopDown { external; /// @notice Transfer child token from top-down composable to address. + /// @param _fromTokenId The owning token to transfer from. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. /// @param _data Additional data with no specified format function safeTransferChild( + uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId, @@ -380,10 +384,12 @@ The return value for `onERC721Received` is the magic value `0x150b7a02` which is #### transferChild ```solidity /// @notice Transfer child token from top-down composable to address. +/// @param _fromTokenId The owning token to transfer from. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. function transferChild( + uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId @@ -401,10 +407,12 @@ ERC721(_childContract).transferFrom(this, _to, _childTokenId); #### safeTransferChild ```solidity /// @notice Transfer child token from top-down composable to address. +/// @param _fromTokenId The owning token to transfer from. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. function safeTransferChild( + uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId @@ -422,11 +430,13 @@ ERC721(_childContract).safeTransferFrom(this, _to, _childTokenId); #### safeTransferChild ```solidity /// @notice Transfer child token from top-down composable to address or other top-down composable. +/// @param _fromTokenId The owning token to transfer from. /// @param _to The address that receives the child token /// @param _childContract The ERC721 contract of the child token. /// @param _childTokenId The tokenId of the token that is being transferred. /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to function safeTransferChild( + uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId, @@ -493,7 +503,7 @@ function getChild( external; ``` -This function is used to transfer an ERC721 token when its contract does not have a `safeTransferChild(address _to, address _childContract, uint256 _childTokenId, bytes _data)` function. +This function is used to transfer an ERC721 token when its contract does not have a `safeTransferChild(uint256 _fromTokenId, address _to, address _childContract, uint256 _childTokenId, bytes _data)` function. A transfer with this function is done in two steps: 1. The owner of the ERC721 token calls `approve` or `setApprovalForAll` in the ERC721 contract for the top-down composable contract. @@ -576,12 +586,12 @@ interface ERC998ERC20TopDown { /// @dev This emits when a token receives ERC20 tokens. /// @param _from The prior owner of the token. - /// @param _tokenId The token that receives the ERC20 tokens. + /// @param _toTokenId The token that receives the ERC20 tokens. /// @param _erc20Contract The ERC20 contract. /// @param _value The number of ERC20 tokens received. event ReceivedERC20( address indexed _from, - uint256 indexed _tokenId, + uint256 indexed _toTokenId, address indexed _erc20Contract, uint256 _value ); @@ -592,7 +602,7 @@ interface ERC998ERC20TopDown { /// @param _erc20Contract The ERC20 contract. /// @param _value The number of ERC20 tokens transferred. event TransferERC20( - uint256 indexed _tokenId, + uint256 indexed _fromTokenId, address indexed _to, address indexed _erc20Contract, uint256 _value From 74d927534903cb51691f12230153cff578fc6211 Mon Sep 17 00:00:00 2001 From: benk10 Date: Wed, 8 Aug 2018 18:44:41 +0300 Subject: [PATCH 096/177] Automatically merged updates to draft EIP(s) 1285 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1285.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-1285.md b/EIPS/eip-1285.md index 8416cf6e..f91c3d6e 100644 --- a/EIPS/eip-1285.md +++ b/EIPS/eip-1285.md @@ -24,18 +24,25 @@ This EIP proposes to increase the given stipend from ``2,300`` to ``3,500`` to i The main motivation behind this EIP is to allow simple fallback functions to be implemented for contracts following the ``"Proxy"`` pattern. Simply explained, a ``"Proxy Contract"`` is a contract which use ``DELEGATECALL`` in its ``fallback`` function to behave according to the logic of another contract and serve as an independent instance for the logic of the contract it points to. This pattern is very useful for saving gas per deployment (as Proxy contracts are very lean) and it opens the ability to experiment with upgradability of contracts. -On average, the ``DELEGATECALL`` functionality of a proxy contract costs about 1,000 gas units. -When a contract transfers ETH to a proxy contract, the proxy logic will consume about 1,000 gas units before the ``fallback`` function of the logic contract will be executed. This leaves merely about 1,300 gas units for the execution of the logic. This is a severe limitation as it is not enough for an average ``LOG`` operation (it might be enough for a ``LOG`` with one parameter). +On average, the ``DELEGATECALL`` functionality of a proxy contract costs about ``1,000`` gas units. +When a contract transfers ETH to a proxy contract, the proxy logic will consume about ``1,000`` gas units before the ``fallback`` function of the logic contract will be executed. This leaves merely about 1,300 gas units for the execution of the logic. This is a severe limitation as it is not enough for an average ``LOG`` operation (it might be enough for a ``LOG`` with one parameter). By slightly increasing the gas units given in the stipend we allow proxy contracts have proper ``fallback`` logic without increasing the attack surface of the calling contract. ## Specification -Increase the ``Gcallstipend`` fee parameter in the ``CALL`` OPCODE from ``2,300`` to ``3,500`` gas units (further specification will be provided later). +Increase the ``Gcallstipend`` fee parameter in the ``CALL`` OPCODE from ``2,300`` to ``3,500`` gas unit. +The actual change to the Ethereum clients would be to change the ``CallStipend`` they store as a constant. +For an implementation example you can find a Geth client implementation linked [here](https://github.com/ben-kaufman/go-ethereum/tree/eip-1285). The actual change to the code can be found [here](https://github.com/ben-kaufman/go-ethereum/blob/eip-1285/params/protocol_params.go#L41). ## Rationale -The rational for increasing the ``Gcallstipend`` gas parameter by ``1,200`` gas units comes from the cost of performing ``DELEGATECALL`` and ``SLOAD`` with a small margin for some small additional operations. All while still keeping the stipend relatively small. +The rational for increasing the ``Gcallstipend`` gas parameter by ``1,200`` gas units comes from the cost of performing ``DELEGATECALL`` and ``SLOAD`` with a small margin for some small additional operations. All while still keeping the stipend relatively small and insufficient for accessing the storage or changing the state. ## Backwards Compatibility This EIP requires a backwards incompatible change for the ``Gcallstipend`` gas parameter in the ``CALL`` OPCODE. + + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 681a524dfb90333d879495c9d32c419e57422433 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 9 Aug 2018 18:09:08 +0800 Subject: [PATCH 097/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 109 ++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index 40bd258c..9cce899d 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -24,37 +24,36 @@ expensive. However, EIP-1087 requires keeping a dirty map for storage changes, and implictly makes the assumption that a transaction's storage changes are committed to the storage trie at the end of a transaction. This works well for some implementations, but not for -others. After EIP-658, some implementations do the optimization to -only commit storage changes at the end of a block. For them, it is -possible to know a storage's original value and current value, but it -is not possible to iterate over all storage changes. For EIP-1087, -they will need to keep a separate dirty map to keep track of gas -costs. This adds additional memory consumptions. +others. After EIP-658, an efficient storage cache implementation would +probably use an in-memory trie (without RLP encoding/decoding) or +other immutable data structures to keep track of storage changes, and +only commit changes at the end of a block. For them, it is possible to +know a storage's original value and current value, but it is not +possible to iterate over all storage changes without incur additional +memory or processing costs. This EIP proposes an alternative way for gas metering on SSTORE, using information that is more universially available to most implementations: -* *Storage slot's original value*. This is the value of the storage if - a call/create reversion happens on the current VM execution - context. It is universially available because all clients need to - keep track of call/create reversion. +* *Storage slot's original value*. * *Storage slot's current value*. * Refund counter. -This EIP indeed has edge cases where it may consume more gases -compared with EIP-1087 (see Rationale), but it can be worth the trade -off: +We provided two possible specification versions here. -* We don't suffer from the optimization limitation of EIP-1087. After - EIP-658, an efficient storage cache implementation would probably - use an in-memory trie (without RLP encoding/decoding) or other - immutable data structures to keep track of storage changes, and only - commit changes at the end of a block. For those implementations, we - cannot efficiently iterate over a transaction's storage change slots - without doing a full diff of the trie. -* It never costs more gases compared with current scheme. -* It covers most common usages. +* For both of the two versions, we don't suffer from the optimization + limitation of EIP-1087, and it never costs more gases compared with + current scheme. +* For Version I, it covers most common usages, and we have two edge + cases it do not cover, one of which may potentially be useful. +* For Version II, it covers nearly all usages for a transient + storage. We only have one rare edge case (resetting a storage back + to its original value and then set it again) not covered. Clients + that are easy to implement EIP-1087 will also be easy to implement + Version II. Some other clients might require a little bit extra + refactoring on this. Nonetheless, no extra memory or processing cost + is needed on runtime. Usages that benefits from this EIP's gas reduction scheme includes: @@ -64,12 +63,21 @@ Usages that benefits from this EIP's gas reduction scheme includes: frame, where this information does not need to be persistent outside of a transaction. This includes sub-frame error codes and message passing, etc. +* Passing storage information from parent call frame to sub call + frame, where this information does not need to be persistent outside + of a transaction. This is covered by Version II but not Version I. -## Specification +## Specification (Version I) -Term *original value* is as defined in Motivation. *Current value* -refers to the storage slot value before SSTORE happens. *New value* -refers to the storage slot value after SSTORE happens. +Definitions of terms are as below: + +* *Storage slot's original value*: This is the value of the storage if + a call/create reversion happens on the *current VM execution + context*. +* *Storage slot's current value*: This is the value of the storage + before SSTORE operation happens. +* *Storage slot's new value*: This is the value of the storage after + SSTORE operation happens. Replace SSTORE opcode gas cost calculation (including refunds) with the following logic: @@ -99,6 +107,14 @@ the following logic: Refund counter works as before -- it is limited to half of the gas consumed. +## Specification (Version II) + +The same as Version I, except we change the definition of *original +value* to: + +* *Storage slot's original value*: This is the value of the storage if + a reversion happens on the *current transaction*. + ## Explanation The new gas cost scheme for SSTORE is divided to three different @@ -128,18 +144,19 @@ been changed (either on current call frame or a sub-call frame for the same contract). When going from **Fresh** to **Dirty**, we charge the gas cost the same as current scheme. -When entering a sub-call frame, a previously-marked **Dirty** storage -slot will again become **Fresh**, but only for this sub-call -frame. Note that we don't charge any more gases compared with current -scheme in this case. +For Version I, when entering a sub-call frame, a previously-marked +**Dirty** storage slot will again become **Fresh**, but only for this +sub-call frame. Note that we don't charge any more gases compared with +current scheme in this case. In current call frame, a **Dirty** storage slot can be reset back to **Fresh** via a SSTORE opcode either on current call frame or a -sub-call frame. For current call frame, this dirtiness is tracked, so -we can issue refunds. For sub-call frame, it is not possible to track -this dirtiness reset, so the refunds (for *current call frame*'s -initial SSTORE from **Fresh** to **Dirty**) are not issued. In the -case where refunds are not issued, the gas cost is the same as the +sub-call frame. For Version I, for current call frame, this dirtiness +is tracked, so we can issue refunds. For sub-call frame, it is not +possible to track this dirtiness reset, so the refunds (for *current +call frame*'s initial SSTORE from **Fresh** to **Dirty**) are not +issued. This refund is tracked on Version II, and issued properly. In +the case where refunds are not issued, the gas cost is the same as the current scheme. When a storage slot remains at **Dirty**, we charge 200 gas. In this @@ -156,9 +173,9 @@ sub-call frame. Below is a graph ([by @Arachnid](https://github.com/ethereum/EIPs/pull/1283#issuecomment-410229053)) -showing possible state transition of gas costs. Note that this applies -to current call frame only, and we ignore **No-op** state because that -is trivial: +showing possible state transition of gas costs. Note that for Version +I, this applies to current call frame only, and we ignore **No-op** +state because that is trivial: ![State Transition](../assets/eip-1283/state.png) @@ -183,12 +200,15 @@ When *original value* is not 0: ## Rationale -This EIP mostly archives what EIP-1087 tries to do, but without the -complexity of introducing the concept of "dirty maps". One limitation -is that for some edge cases dirtiness will not be tracked: +This EIP mostly archives what a transient storage tries to do +(EIP-1087 and EIP-1153), but without the complexity of introducing the +concept of "dirty maps", or an extra storage struct. One limitation is +that for some edge cases dirtiness will not be tracked: -* The first SSTORE for a storage slot on a sub-call frame for the same - contract won't benefit from gas reduction. +* For Version I, the first SSTORE for a storage slot on a sub-call + frame for the same contract won't benefit from gas reduction. For + Version II, this type of gas reduction is properly tracked and + applied. * If a storage slot is changed, and it's reset to its original value. The next SSTORE to the same storage slot won't benefit from gas reduction. @@ -219,7 +239,8 @@ To be added. ## Implementation -To be added. +* Parity Ethereum: [Version I + PR](https://github.com/paritytech/parity-ethereum/pull/9319). ## Copyright From 7ecab6a33e57e6fd0270122c661a3ff264abea67 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Fri, 10 Aug 2018 03:03:01 +0800 Subject: [PATCH 099/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index 9cce899d..6c56afdc 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -151,13 +151,13 @@ current scheme in this case. In current call frame, a **Dirty** storage slot can be reset back to **Fresh** via a SSTORE opcode either on current call frame or a -sub-call frame. For Version I, for current call frame, this dirtiness -is tracked, so we can issue refunds. For sub-call frame, it is not +sub-call frame. For current call frame, this dirtiness is tracked, so +we can issue refunds. For Version I's sub-call frame, it is not possible to track this dirtiness reset, so the refunds (for *current call frame*'s initial SSTORE from **Fresh** to **Dirty**) are not -issued. This refund is tracked on Version II, and issued properly. In -the case where refunds are not issued, the gas cost is the same as the -current scheme. +issued. For Version II's sub-call frame, This refund is tracked and +properly issued. In the case where refunds are not issued, the gas +cost is the same as the current scheme. When a storage slot remains at **Dirty**, we charge 200 gas. In this case, we would also need to keep track of `R_SCLEAR` refunds -- if we From cbca62cdc521c711e661fe6b202437164b4e258c Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Fri, 10 Aug 2018 17:32:24 +0800 Subject: [PATCH 100/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index 6c56afdc..cc340e01 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -43,7 +43,7 @@ implementations: We provided two possible specification versions here. * For both of the two versions, we don't suffer from the optimization - limitation of EIP-1087, and it never costs more gases compared with + limitation of EIP-1087, and it never costs more gas compared with current scheme. * For Version I, it covers most common usages, and we have two edge cases it do not cover, one of which may potentially be useful. @@ -146,7 +146,7 @@ gas cost the same as current scheme. For Version I, when entering a sub-call frame, a previously-marked **Dirty** storage slot will again become **Fresh**, but only for this -sub-call frame. Note that we don't charge any more gases compared with +sub-call frame. Note that we don't charge any more gas compared with current scheme in this case. In current call frame, a **Dirty** storage slot can be reset back to From b569c4fa82cf3dcd33209e1ede97fc2853e2a187 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Fri, 10 Aug 2018 23:33:58 +0800 Subject: [PATCH 101/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index cc340e01..09b8168f 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -40,7 +40,8 @@ implementations: * *Storage slot's current value*. * Refund counter. -We provided two possible specification versions here. +We provided two possible specification versions here. Version II is +recommended for Constantinople. * For both of the two versions, we don't suffer from the optimization limitation of EIP-1087, and it never costs more gas compared with @@ -109,7 +110,8 @@ consumed. ## Specification (Version II) -The same as Version I, except we change the definition of *original +This is the recommended version for Constantinople. Everything else +is the same as Version I, except we change the definition of *original value* to: * *Storage slot's original value*: This is the value of the storage if From 6ecbcaa6e9bb1c6d69e3f462d1e3c9b078e68e47 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Fri, 10 Aug 2018 23:42:25 +0800 Subject: [PATCH 102/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index 09b8168f..904aef15 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -49,12 +49,10 @@ recommended for Constantinople. * For Version I, it covers most common usages, and we have two edge cases it do not cover, one of which may potentially be useful. * For Version II, it covers nearly all usages for a transient - storage. We only have one rare edge case (resetting a storage back - to its original value and then set it again) not covered. Clients - that are easy to implement EIP-1087 will also be easy to implement - Version II. Some other clients might require a little bit extra - refactoring on this. Nonetheless, no extra memory or processing cost - is needed on runtime. + storage. Clients that are easy to implement EIP-1087 will also be + easy to implement Version II. Some other clients might require a + little bit extra refactoring on this. Nonetheless, no extra memory + or processing cost is needed on runtime. Usages that benefits from this EIP's gas reduction scheme includes: @@ -211,9 +209,9 @@ that for some edge cases dirtiness will not be tracked: frame for the same contract won't benefit from gas reduction. For Version II, this type of gas reduction is properly tracked and applied. -* If a storage slot is changed, and it's reset to its original - value. The next SSTORE to the same storage slot won't benefit from - gas reduction. +* If a storage slot is changed, and it's reset to its original value, + the next SSTORE will effectively move gas to refund counter. This + case still benefits from this EIP's gas reduction. Examine examples provided in EIP-1087's Motivation: @@ -226,9 +224,10 @@ Examine examples provided in EIP-1087's Motivation: ending balances * If the token contract has multi-send function, it will cost `5000 * 3 + 200 - 4800 = 10400` gas. - * If this transfer from A to B to C is invoked by a third-party - contract, and the token contract has no multi-send function, then - it won't benefit from this EIP's gas reduction. + * For Version I, if this transfer from A to B to C is invoked by a + third-party contract, and the token contract has no multi-send + function, then it won't benefit from this EIP's gas reduction. For + Version II, this gas reduction is properly tracked and applied. ## Backwards Compatibility From 2933084869e8b3024a98bb403db4a8661e1bb901 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Sun, 12 Aug 2018 06:32:09 +0800 Subject: [PATCH 103/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 101 ++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 72 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index 904aef15..d622db50 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -40,19 +40,15 @@ implementations: * *Storage slot's current value*. * Refund counter. -We provided two possible specification versions here. Version II is -recommended for Constantinople. +For the specification provided here: -* For both of the two versions, we don't suffer from the optimization - limitation of EIP-1087, and it never costs more gas compared with - current scheme. -* For Version I, it covers most common usages, and we have two edge - cases it do not cover, one of which may potentially be useful. -* For Version II, it covers nearly all usages for a transient - storage. Clients that are easy to implement EIP-1087 will also be - easy to implement Version II. Some other clients might require a - little bit extra refactoring on this. Nonetheless, no extra memory - or processing cost is needed on runtime. +* We don't suffer from the optimization limitation of EIP-1087, and it + never costs more gas compared with current scheme. +* It covers all usages for a transient storage. Clients that are easy + to implement EIP-1087 will also be easy to implement this + specification. Some other clients might require a little bit extra + refactoring on this. Nonetheless, no extra memory or processing cost + is needed on runtime. Usages that benefits from this EIP's gas reduction scheme includes: @@ -66,13 +62,12 @@ Usages that benefits from this EIP's gas reduction scheme includes: frame, where this information does not need to be persistent outside of a transaction. This is covered by Version II but not Version I. -## Specification (Version I) +## Specification Definitions of terms are as below: * *Storage slot's original value*: This is the value of the storage if - a call/create reversion happens on the *current VM execution - context*. + a reversion happens on the *current transaction*. * *Storage slot's current value*: This is the value of the storage before SSTORE operation happens. * *Storage slot's new value*: This is the value of the storage after @@ -106,15 +101,6 @@ the following logic: Refund counter works as before -- it is limited to half of the gas consumed. -## Specification (Version II) - -This is the recommended version for Constantinople. Everything else -is the same as Version I, except we change the definition of *original -value* to: - -* *Storage slot's original value*: This is the value of the storage if - a reversion happens on the *current transaction*. - ## Explanation The new gas cost scheme for SSTORE is divided to three different @@ -123,14 +109,11 @@ types: * **No-op**: the virtual machine does not need to do anything. This is the case if *current value* equals *new value*. * **Fresh**: this storage slot has not been changed, or has been reset - to its original value either on current frame, or on a sub-call - frame for the same contract. This is the case if *current value* - does not equal *new value*, and *original value* equals *current - value*. -* **Dirty**: this storage slot has already been changed, either on - current frame or on a sub-call frame for the same contract. This is - the case if *current value* does not equal *new value*, and - *original value* does not equal *current value*. + to its original value. This is the case if *current value* does not + equal *new value*, and *original value* equals *current value*. +* **Dirty**: this storage slot has already been changed. This is the + case if *current value* does not equal *new value*, and *original + value* does not equal *current value*. We can see that the above three types cover all possible variations of *original value*, *current value*, and *new value*. @@ -140,24 +123,10 @@ We can see that the above three types cover all possible variations of All initial (not-**No-op**) SSTORE on a particular storage slot starts with **Fresh**. After that, it will become **Dirty** if the value has -been changed (either on current call frame or a sub-call frame for the -same contract). When going from **Fresh** to **Dirty**, we charge the -gas cost the same as current scheme. - -For Version I, when entering a sub-call frame, a previously-marked -**Dirty** storage slot will again become **Fresh**, but only for this -sub-call frame. Note that we don't charge any more gas compared with -current scheme in this case. - -In current call frame, a **Dirty** storage slot can be reset back to -**Fresh** via a SSTORE opcode either on current call frame or a -sub-call frame. For current call frame, this dirtiness is tracked, so -we can issue refunds. For Version I's sub-call frame, it is not -possible to track this dirtiness reset, so the refunds (for *current -call frame*'s initial SSTORE from **Fresh** to **Dirty**) are not -issued. For Version II's sub-call frame, This refund is tracked and -properly issued. In the case where refunds are not issued, the gas -cost is the same as the current scheme. +been changed. When going from **Fresh** to **Dirty**, we charge the +gas cost the same as current scheme. A **Dirty** storage slot can be +reset back to **Fresh** via a SSTORE opcode. This will trigger a +refund. When a storage slot remains at **Dirty**, we charge 200 gas. In this case, we would also need to keep track of `R_SCLEAR` refunds -- if we @@ -166,15 +135,13 @@ already issued the refund but it no longer applies (*current value* is issue the refund but it applies now (*new value* is 0), then adds this refund to the refund counter. It is not possible where a refund is not issued but we remove the refund in the above case, because all storage -slot starts with **Fresh** state, either on current call frame or a -sub-call frame. +slot starts with **Fresh** state. ### State Transition Below is a graph ([by @Arachnid](https://github.com/ethereum/EIPs/pull/1283#issuecomment-410229053)) -showing possible state transition of gas costs. Note that for Version -I, this applies to current call frame only, and we ignore **No-op** +showing possible state transition of gas costs. We ignore **No-op** state because that is trivial: ![State Transition](../assets/eip-1283/state.png) @@ -202,16 +169,13 @@ When *original value* is not 0: This EIP mostly archives what a transient storage tries to do (EIP-1087 and EIP-1153), but without the complexity of introducing the -concept of "dirty maps", or an extra storage struct. One limitation is -that for some edge cases dirtiness will not be tracked: +concept of "dirty maps", or an extra storage struct. -* For Version I, the first SSTORE for a storage slot on a sub-call - frame for the same contract won't benefit from gas reduction. For - Version II, this type of gas reduction is properly tracked and - applied. -* If a storage slot is changed, and it's reset to its original value, - the next SSTORE will effectively move gas to refund counter. This - case still benefits from this EIP's gas reduction. +* For *absolute gas used* (that is, actual *gas used* minus *refund*), + this EIP is equivalent to EIP-1087 for all cases. +* For one particular case, where a storage slot is changed, reset to + its original value, and then changed again, EIP-1283 would move more + gases to refund counter compared with EIP-1087. Examine examples provided in EIP-1087's Motivation: @@ -221,13 +185,7 @@ Examine examples provided in EIP-1087's Motivation: charged `20000 + 5 * 200 = 21000` gas. * A balance transfer from account A to account B followed by a transfer from B to C, with all accounts having nonzero starting and - ending balances - * If the token contract has multi-send function, it will cost - `5000 * 3 + 200 - 4800 = 10400` gas. - * For Version I, if this transfer from A to B to C is invoked by a - third-party contract, and the token contract has no multi-send - function, then it won't benefit from this EIP's gas reduction. For - Version II, this gas reduction is properly tracked and applied. + ending balances, it will cost `5000 * 3 + 200 - 4800 = 10400` gas. ## Backwards Compatibility @@ -240,8 +198,7 @@ To be added. ## Implementation -* Parity Ethereum: [Version I - PR](https://github.com/paritytech/parity-ethereum/pull/9319). +To be added. ## Copyright From bf5c7811b7f6a3d41a69d959489d63e7ff9fe9d2 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Sun, 12 Aug 2018 18:24:14 +0800 Subject: [PATCH 104/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index d622db50..add77e1b 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -54,13 +54,10 @@ Usages that benefits from this EIP's gas reduction scheme includes: * Subsequent storage write operations within the same call frame. This includes reentry locks, same-contract multi-send, etc. -* Passing storage information from sub call frame to parent call +* Exchange storage information between sub call frame and parent call frame, where this information does not need to be persistent outside of a transaction. This includes sub-frame error codes and message passing, etc. -* Passing storage information from parent call frame to sub call - frame, where this information does not need to be persistent outside - of a transaction. This is covered by Version II but not Version I. ## Specification From a568bf326f1f9459ead59886740fe4286a37047e Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Mon, 13 Aug 2018 04:07:33 +0200 Subject: [PATCH 105/177] Automatically merged updates to draft EIP(s) 820 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-820.md | 755 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 614 insertions(+), 141 deletions(-) diff --git a/EIPS/eip-820.md b/EIPS/eip-820.md index 1cc8eff5..3dda93b8 100644 --- a/EIPS/eip-820.md +++ b/EIPS/eip-820.md @@ -1,6 +1,6 @@ --- eip: 820 -title: Pseudo-introspection registry contract +title: Pseudo-introspection Registry Contract author: Jordi Baylina , Jacques Dafflon discussions-to: https://github.com/ethereum/EIPs/issues/820 status: Draft @@ -11,165 +11,200 @@ created: 2018-01-05 ## Simple Summary -This standard defines a universal registry smart contract where any address (contract or regular account) can register which interface it implements and which smart contract is responsible for its implementation. +This standard defines a universal registry smart contract where any address (contract or regular account) can register which interface it supports and which smart contract is responsible for its implementation. -This standard keeps backwards compatibility with [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) +This standard keeps backwards compatibility with [ERC165]. ## Abstract -This standard attempts to define a registry where smart contracts and regular accounts can publish which functionalities they implement. +This standard attempts to define a registry where smart contracts and regular accounts can publish which functionalities they implement—either directly or through a proxy contract . The rest of the world can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation. -This registry can be deployed on any chain and will share the exact same address. +This registry MAY be deployed on any chain and will share the exact same address. -Interfaces where the last 28 bytes are `0` are considered [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) interfaces, and this registry -will forward the call to the contract to see if they implement that interface. +Interfaces with zeroes (`0`) as the the last 28 bytes are considered [ERC165] interfaces, and this registry SHALL forward the call to the contract to see if it implements the interface. -This contract will act also as an [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) cache in order to safe gas. +This contract also acts as an [ERC165] cache in order to reduce gas consumption. ## Motivation -There has been different approaches to define pseudo-introspection in the Ethereum. The first is [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) which has the problem that it is not available for regular accounts to use. The second approach is EIP-672 which uses reverseENS. Using reverseENS, has two issues. First, it is unnecessarily complex, and second, ENS is still a centralized contract controlled by a multisig. This multisig, theoretically would be able to modify the system. +There has been different approaches to define pseudo-introspection in Ethereum. The first is [ERC165] which has the limitation that it cannot be used by regular accounts. The second attempt is [ERC672] which uses reverseENS. Using reverseENS has two issues. First, it is unnecessarily complex, and second, ENS is still a centralized contract controlled by a multisig. This multisig, theoretically would be able to modify the system. -This standard is much simpler than [EIP-672](https://github.com/ethereum/EIPs/issues/672) and it is absolutely decentralized. +This standard is much simpler than [ERC672] and it is fully decentralized. -This standard also solves the problem of having different addresses for different chains. +This standard also provides a unique address for all chains. Thus solving the problem of resolving the correct registry address for different chains. ## Specification -### The smart contract +### [ERC820] Registry Smart Contract -```solidity +> This is an exact copy of the code of the [ERC820 registry smart contract]. + +``` solidity pragma solidity 0.4.24; +// IV is a simply value to have a "nice" address for the registry, starting with `0x820`. +// IV: 12302 +/// @dev The interface a contract MUST implement if it is the implementer of +/// some interface for any address other than itself. interface ERC820ImplementerInterface { - /// @notice Contracts that implement an interferce in behalf of another contract must return true - /// @param addr Address that the contract woll implement the interface in behalf of - /// @param interfaceHash keccak256 of the name of the interface - /// @return ERC820_ACCEPT_MAGIC if the contract can implement the interface represented by - /// `ìnterfaceHash` in behalf of `addr` - function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) view public returns(bytes32); + /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not. + /// @param addr Address for which the contract will implement the interface + /// @param interfaceHash keccak256 hash of the name of the interface + /// @return ERC820_ACCEPT_MAGIC only if the contract implements `ìnterfaceHash` for the address `addr`. + function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32); } +/// @title ERC820 Pseudo-introspection Registry Contract +/// @author Jordi Baylina and Jacques Dafflon +/// @notice This contract is the official implementation of the ERC820 Registry. +/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820 contract ERC820Registry { - bytes4 constant InvalidID = 0xffffffff; + /// @notice ERC165 Invalid ID. + bytes4 constant INVALID_ID = 0xffffffff; + /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`). bytes4 constant ERC165ID = 0x01ffc9a7; - bytes32 constant ERC820_ACCEPT_MAGIC = keccak256("ERC820_ACCEPT_MAGIC"); + /// @notice Magic value which is returned if a contrct implements an interface on behalf of some other address. + bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC820_ACCEPT_MAGIC")); mapping (address => mapping(bytes32 => address)) interfaces; mapping (address => address) managers; - mapping (address => mapping(bytes4 => bool)) erc165Cache; + mapping (address => mapping(bytes4 => bool)) erc165Cached; + /// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`. event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer); + /// @notice Indicates `newManager` is the address of the new manager for `addr`. event ManagerChanged(address indexed addr, address indexed newManager); - /// @notice Query the hash of an interface given a name - /// @param interfaceName Name of the interfce - function interfaceHash(string interfaceName) public pure returns(bytes32) { - return keccak256(interfaceName); + /// @notice Query if an address implements an interface and through which contract. + /// @param _addr Address being queried for the implementer of an interface. + /// (If `_addr == 0` then `msg.sender` is assumed.) + /// @param _interfaceHash keccak256 hash of the name of the interface as a string. + /// E.g., `web3.utils.keccak256('ERC777Token')`. + /// @return The address of the contract which implements the interface `_interfaceHash` for `_addr` + /// or `0x0` if `_addr` did not register an implementer for this interface. + function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) { + address addr = _addr == 0 ? msg.sender : _addr; + if (isERC165Interface(_interfaceHash)) { + bytes4 erc165InterfaceHash = bytes4(_interfaceHash); + return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0; + } + return interfaces[addr][_interfaceHash]; } - /// @notice GetManager - function getManager(address addr) public view returns(address) { + /// @notice Sets the contract which implements a specific interface for an address. + /// Only the manager defined for that address can set it. + /// (Each address is the manager for itself until it sets a new manager.) + /// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.) + /// @param _interfaceHash keccak256 hash of the name of the interface as a string. + /// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface. + function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external { + address addr = _addr == 0 ? msg.sender : _addr; + require(getManager(addr) == msg.sender, "Not the manager"); + + require(!isERC165Interface(_interfaceHash), "Must not be a ERC165 hash"); + if (_implementer != 0 && _implementer != msg.sender) { + require( + ERC820ImplementerInterface(_implementer) + .canImplementInterfaceForAddress(addr, _interfaceHash) == ERC820_ACCEPT_MAGIC, + "Does not implement the interface" + ); + } + interfaces[addr][_interfaceHash] = _implementer; + emit InterfaceImplementerSet(addr, _interfaceHash, _implementer); + } + + /// @notice Sets the `_newManager` as manager for the `_addr` address. + /// The new manager will be able to call `setInterfaceImplementer` for `_addr`. + /// @param _addr Address for which to set the new manager. (If `_addr == 0` then `msg.sender` is assumed.) + /// @param _newManager Address of the new manager for `addr`. (Pass `0x0` to reset the manager to `_addr` itself.) + function setManager(address _addr, address _newManager) external { + address addr = _addr == 0 ? msg.sender : _addr; + require(getManager(addr) == msg.sender, "Not the manager"); + managers[addr] = _newManager == addr ? 0 : _newManager; + emit ManagerChanged(addr, _newManager); + } + + /// @notice Get the manager of an address. + /// @param _addr Address for which to return the manager. + /// @return Address of the manager for a given address. + function getManager(address _addr) public view returns(address) { // By default the manager of an address is the same address - if (managers[addr] == 0) { - return addr; + if (managers[_addr] == 0) { + return _addr; } else { - return managers[addr]; + return managers[_addr]; } } - /// @notice Sets an external `manager` that will be able to call `setInterfaceImplementer()` - /// on behalf of the address. - /// @param _addr Address that you are defining the manager for. (0x0 if is msg.sender) - /// @param newManager The address of the manager for the `addr` that will replace - /// the old one. Set to 0x0 if you want to remove the manager. - function setManager(address _addr, address newManager) public { - address addr = _addr == 0 ? msg.sender : _addr; - require(getManager(addr) == msg.sender); - managers[addr] = newManager == addr ? 0 : newManager; - ManagerChanged(addr, newManager); + /// @notice Compute the keccak256 hash of an interface given its name. + /// @param _interfaceName Name of the interface. + /// @return The keccak256 hash of an interface name. + function interfaceHash(string _interfaceName) public pure returns(bytes32) { + return keccak256(abi.encodePacked(_interfaceName)); } - /// @notice Query if an address implements an interface and thru which contract - /// @param _addr Address that is being queried for the implementation of an interface - /// (if _addr == 0 them `msg.sender` is assumed) - /// @param iHash SHA3 of the name of the interface as a string - /// Example `web3.utils.sha3('ERC777Token`')` - /// @return The address of the contract that implements a specific interface - /// or 0x0 if `addr` does not implement this interface - function getInterfaceImplementer(address _addr, bytes32 iHash) constant public returns (address) { - address addr = _addr == 0 ? msg.sender : _addr; - if (isERC165Interface(iHash)) { - bytes4 i165Hash = bytes4(iHash); - return erc165InterfaceSupported(addr, i165Hash) ? addr : 0; - } - return interfaces[addr][iHash]; - } + /* --- ERC165 Related Functions --- */ - /// @notice Sets the contract that will handle a specific interface; only - /// a `manager` defined for that address can set it. - /// ( Each address is the manager for itself until a new manager is defined) - /// @param _addr Address that you want to define the interface for - /// (if _addr == 0 them `msg.sender` is assumed) - /// @param iHash SHA3 of the name of the interface as a string - /// For example `web3.utils.sha3('Ierc777')` for the Ierc777 - function setInterfaceImplementer(address _addr, bytes32 iHash, address implementer) public { - address addr = _addr == 0 ? msg.sender : _addr; - require(getManager(addr) == msg.sender); - - require(!isERC165Interface(iHash)); - if ((implementer != 0) && (implementer!=msg.sender)) { - require(ERC820ImplementerInterface(implementer).canImplementInterfaceForAddress(addr, iHash) - == ERC820_ACCEPT_MAGIC); - } - interfaces[addr][iHash] = implementer; - InterfaceImplementerSet(addr, iHash, implementer); - } - - -/// ERC165 Specific - - function isERC165Interface(bytes32 iHash) internal pure returns (bool) { - return iHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0; - } - - function erc165InterfaceSupported(address _contract, bytes4 _interfaceId) constant public returns (bool) { - if (!erc165Cache[_contract][_interfaceId]) { - erc165UpdateCache(_contract, _interfaceId); + /// @notice Checks whether a contract implements an ERC165 interface or not. + /// The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`. + /// @param _contract Address of the contract to check. + /// @param _interfaceId ERC165 interface to check. + /// @return `true` if `_contract` implements `_interfaceId`, false otherwise. + /// @dev This function may modify the state when updating the cache. However, this function must have the `view` + /// modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the ERC165 cache + /// is updated. + function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) { + if (!erc165Cached[_contract][_interfaceId]) { + updateERC165Cache(_contract, _interfaceId); } return interfaces[_contract][_interfaceId] != 0; } - function erc165UpdateCache(address _contract, bytes4 _interfaceId) public { - interfaces[_contract][_interfaceId] = - erc165InterfaceSupported_NoCache(_contract, _interfaceId) ? _contract : 0; - erc165Cache[_contract][_interfaceId] = true; + /// @notice Updates the cache with whether contract implements an ERC165 interface or not. + /// @param _contract Address of the contract for which to update the cache. + /// @param _interfaceId ERC165 interface for which to update the cache. + function updateERC165Cache(address _contract, bytes4 _interfaceId) public { + interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0; + erc165Cached[_contract][_interfaceId] = true; } - function erc165InterfaceSupported_NoCache(address _contract, bytes4 _interfaceId) public constant returns (bool) { + /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. + /// @param _contract Address of the contract to check. + /// @param _interfaceId ERC165 interface to check. + /// @return `true` if `_contract` implements `_interfaceId`, false otherwise. + function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) { uint256 success; uint256 result; (success, result) = noThrowCall(_contract, ERC165ID); - if ((success==0)||(result==0)) { + if (success == 0 || result == 0) { return false; } - (success, result) = noThrowCall(_contract, InvalidID); - if ((success==0)||(result!=0)) { + (success, result) = noThrowCall(_contract, INVALID_ID); + if (success == 0 || result != 0) { return false; } (success, result) = noThrowCall(_contract, _interfaceId); - if ((success==1)&&(result==1)) { + if (success == 1 && result == 1) { return true; } return false; } - function noThrowCall(address _contract, bytes4 _interfaceId) constant internal returns (uint256 success, uint256 result) { + /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not. + /// @param _interfaceHash The hash to check. + /// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise. + function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) { + return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0; + } + + function noThrowCall(address _contract, bytes4 _interfaceId) + internal view returns (uint256 success, uint256 result) + { bytes4 erc165ID = ERC165ID; assembly { @@ -178,99 +213,537 @@ contract ERC820Registry { mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature success := staticcall( - 30000, // 30k gas - _contract, // To addr - x, // Inputs are stored at location x - 0x08, // Inputs are 8 bytes long - x, // Store output over input (saves space) - 0x20) // Outputs are 32 bytes long + 30000, // 30k gas + _contract, // To addr + x, // Inputs are stored at location x + 0x08, // Inputs are 8 bytes long + x, // Store output over input (saves space) + 0x20 // Outputs are 32 bytes long + ) result := mload(x) // Load the result } } } -``` - -### Raw transaction for deploying the smart contract on any chain ``` -0xf908b78085174876e800830c35008080b90864608060405234801561001057600080fd5b50610844806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf578063571a1f66146100fc5780635df8122f1461012a57806365ba36c11461015157806390e47957146101bc578063aabbb8ca146101fe578063ddc23ddd14610222575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610250565b005b3480156100cb57600080fd5b506100e0600160a060020a0360043516610402565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a0360043516600160e060020a03196024351661044e565b34801561013657600080fd5b506100bd600160a060020a03600435811690602435166104d8565b34801561015d57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526101aa9436949293602493928401919081908401838280828437509497506105a09650505050505050565b60408051918252519081900360200190f35b3480156101c857600080fd5b506101ea600160a060020a0360043516600160e060020a031960243516610604565b604080519115158252519081900360200190f35b34801561020a57600080fd5b506100e0600160a060020a036004351660243561067a565b34801561022e57600080fd5b506101ea600160a060020a0360043516600160e060020a0319602435166106f4565b6000600160a060020a038416156102675783610269565b335b90503361027582610402565b600160a060020a03161461028857600080fd5b610291836107a9565b1561029b57600080fd5b600160a060020a038216158015906102bc5750600160a060020a0382163314155b1561039157604080517f4552433832305f4143434550545f4d4147494300000000000000000000000000815281519081900360130181207ff0083250000000000000000000000000000000000000000000000000000000008252600160a060020a038481166004840152602483018790529251909285169163f00832509160448083019260209291908290030181600087803b15801561035b57600080fd5b505af115801561036f573d6000803e3d6000fd5b505050506040513d602081101561038557600080fd5b50511461039157600080fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03808216600090815260016020526040812054909116151561042c575080610449565b50600160a060020a03808216600090815260016020526040902054165b919050565b61045882826106f4565b610463576000610465565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b6000600160a060020a038316156104ef57826104f1565b335b9050336104fd82610402565b600160a060020a03161461051057600080fd5b80600160a060020a031682600160a060020a03161461052f5781610532565b60005b600160a060020a03828116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519185169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a3505050565b6000816040518082805190602001908083835b602083106105d25780518252601f1990920191602091820191016105b3565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff16151561064557610645838361044e565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b60008080600160a060020a038516156106935784610695565b335b91506106a0846107a9565b156106c55750826106b18282610604565b6106bc5760006106be565b815b92506106ec565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b60008080610722857f01ffc9a7000000000000000000000000000000000000000000000000000000006107cb565b9092509050811580610732575080155b1561074057600092506106ec565b61075285600160e060020a03196107cb565b909250905081158061076357508015155b1561077157600092506106ec565b61077b85856107cb565b90925090506001821480156107905750806001145b1561079e57600192506106ec565b506000949350505050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a72305820ba6e246cbdcaf97334eb3dd1ac11e6490103d5ead3f963b5f312a729e88cf9db00291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +### Deployment Transaction + +Below is the raw transaction which MUST be used to deploy the smart contract on any chain. + +``` +0xf90ab18085174876e800830c35008080b90a5e608060405234801561001057600080fd5b50610a3e806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf5780635df8122f146100fc57806365ba36c114610123578063a41e7d511461018e578063aabbb8ca146101bc578063b7056765146101e0578063f712f3e814610222575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610250565b005b3480156100cb57600080fd5b506100e0600160a060020a036004351661054b565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a0360043581169060243516610597565b34801561012f57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261017c9436949293602493928401919081908401838280828437509497506106aa9650505050505050565b60408051918252519081900360200190f35b34801561019a57600080fd5b506100bd600160a060020a0360043516600160e060020a031960243516610774565b3480156101c857600080fd5b506100e0600160a060020a03600435166024356107fe565b3480156101ec57600080fd5b5061020e600160a060020a0360043516600160e060020a031960243516610878565b604080519115158252519081900360200190f35b34801561022e57600080fd5b5061020e600160a060020a0360043516600160e060020a03196024351661092d565b6000600160a060020a038416156102675783610269565b335b9050336102758261054b565b600160a060020a0316146102d3576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6102dc836109a3565b15610331576040805160e560020a62461bcd02815260206004820152601960248201527f4d757374206e6f74206265206120455243313635206861736800000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103525750600160a060020a0382163314155b156104da5760405160200180807f4552433832305f4143434550545f4d414749430000000000000000000000000081525060130190506040516020818303038152906040526040518082805190602001908083835b602083106103c65780518252601f1990920191602091820191016103a7565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207ff0083250000000000000000000000000000000000000000000000000000000008352600160a060020a038881166004850152602484018b90529451909650938816945063f0083250936044808401945091929091908290030181600087803b15801561045957600080fd5b505af115801561046d573d6000803e3d6000fd5b505050506040513d602081101561048357600080fd5b5051146104da576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a038082166000908152600160205260408120549091161515610575575080610592565b50600160a060020a03808216600090815260016020526040902054165b919050565b6000600160a060020a038316156105ae57826105b0565b335b9050336105bc8261054b565b600160a060020a03161461061a576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b80600160a060020a031682600160a060020a031614610639578161063c565b60005b600160a060020a03828116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519185169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a3505050565b6000816040516020018082805190602001908083835b602083106106df5780518252601f1990920191602091820191016106c0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106107425780518252601f199092019160209182019101610723565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b61077e8282610878565b61078957600061078b565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b60008080600160a060020a038516156108175784610819565b335b9150610824846109a3565b15610849575082610835828261092d565b610840576000610842565b815b9250610870565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b600080806108a6857f01ffc9a7000000000000000000000000000000000000000000000000000000006109c5565b90925090508115806108b6575080155b156108c45760009250610870565b6108d685600160e060020a03196109c5565b90925090508115806108e757508015155b156108f55760009250610870565b6108ff85856109c5565b90925090506001821480156109145750806001145b156109225760019250610870565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff16151561096e5761096e8383610774565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a723058209f4f0ded519f65ffc31c58c2e588b1713ee5a922e97928e2ccbd91e39128037700291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ``` -You can see the string of `a`s at the end of the transaction. This is the `s` of the signature, meaning that its a deterministic by hand forced signature. +The string of `a`'s at the end of the transaction is the `s` of the signature. From this pattern, one can clearly deduce that it is a deterministic signature generated by a human. -### Deployment method +### Deployment Method -This contract is going to be deployed using the Nick's Method. +This contract is going to be deployed using [Nick]'s Method. This method works as follows: -This method works as follows: +1. Generate a transaction which deploys the contract from a new random account. + - This transaction MUST NOT use [ERC155] in order to work on any chain. + - This transaction MUST have a relatively high gas price in order to be deployed on any chain. In this case, it is going to be 100 Gwei. + +2. Set the `v`, `r`, `s` of the transaction signature to the following values: + + ``` + v: 27 + r: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 + s: 0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ``` + +This nice `s` value is a "random number" generated deterministically by a human. + +3. We recover the sender of this transaction. Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account. -1. Generate a transaction that deploys the contract from a new random account. This transaction must not use [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) so it can work on any chain. This transaction needs to also have a relatively high gas price in order to be deployed in any chain. In this case, it's going to be 100Gwei. -2. Set the `v`, `r`, `s` of the transaction signature to the following values: -` - v: 27` -` - r: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798` -` - s: 0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` -This nice `s` value is a random number generated deterministically by a human. -3. We recover the sender of this transaction. We will have an account that can broadcast that transaction, but we also have the waranty that nobody knows the private key of that account. 4. Send Ether to this deployment account. + 5. Broadcast the transaction. -This operation can be done in any chain, guaranteed that the contract address is going to always be the same and nobody will be able to mess up that address with a different contract. +This operation can be done in any chain, guaranteeing that the contract address is going to always be the same and nobody will be able to mess up that address with a different contract. -### Special registry deployment account +### Special Registry Deployment Account ``` -0xe515e6d0e6b09a60e3cb50c6a8d51d3009ad18af +0x6c3FB9d29823aB78F93d46C8bF40Abf7a70ED9be ``` -This account is generated by reverse engineering it from it's signature for the transaction, in this way no one knows the private key, but it is known that it's the valid signer of the deployment transaction. +This account is generated by reverse engineering it from its signature for the transaction. This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction. ### Deployed contract ``` -0xbe78655dff872d22b95ae366fb3477d977328ade +0x820A6e5561A66f60c6546a74001db369bbFf238D ``` -The contract will have this address for every chain it is deployed to. +The contract has the address above for every chain it is deployed to. +

    +Raw metadata of ./contracts/ERC820Registry.sol +
    {
    +  "compiler": {
    +    "version": "0.4.24+commit.e67f0147"
    +  },
    +  "language": "Solidity",
    +  "output": {
    +    "abi": [
    +      {
    +        "constant": false,
    +        "inputs": [
    +          {
    +            "name": "_addr",
    +            "type": "address"
    +          },
    +          {
    +            "name": "_interfaceHash",
    +            "type": "bytes32"
    +          },
    +          {
    +            "name": "_implementer",
    +            "type": "address"
    +          }
    +        ],
    +        "name": "setInterfaceImplementer",
    +        "outputs": [],
    +        "payable": false,
    +        "stateMutability": "nonpayable",
    +        "type": "function"
    +      },
    +      {
    +        "constant": true,
    +        "inputs": [
    +          {
    +            "name": "_addr",
    +            "type": "address"
    +          }
    +        ],
    +        "name": "getManager",
    +        "outputs": [
    +          {
    +            "name": "",
    +            "type": "address"
    +          }
    +        ],
    +        "payable": false,
    +        "stateMutability": "view",
    +        "type": "function"
    +      },
    +      {
    +        "constant": false,
    +        "inputs": [
    +          {
    +            "name": "_addr",
    +            "type": "address"
    +          },
    +          {
    +            "name": "_newManager",
    +            "type": "address"
    +          }
    +        ],
    +        "name": "setManager",
    +        "outputs": [],
    +        "payable": false,
    +        "stateMutability": "nonpayable",
    +        "type": "function"
    +      },
    +      {
    +        "constant": true,
    +        "inputs": [
    +          {
    +            "name": "_interfaceName",
    +            "type": "string"
    +          }
    +        ],
    +        "name": "interfaceHash",
    +        "outputs": [
    +          {
    +            "name": "",
    +            "type": "bytes32"
    +          }
    +        ],
    +        "payable": false,
    +        "stateMutability": "pure",
    +        "type": "function"
    +      },
    +      {
    +        "constant": false,
    +        "inputs": [
    +          {
    +            "name": "_contract",
    +            "type": "address"
    +          },
    +          {
    +            "name": "_interfaceId",
    +            "type": "bytes4"
    +          }
    +        ],
    +        "name": "updateERC165Cache",
    +        "outputs": [],
    +        "payable": false,
    +        "stateMutability": "nonpayable",
    +        "type": "function"
    +      },
    +      {
    +        "constant": true,
    +        "inputs": [
    +          {
    +            "name": "_addr",
    +            "type": "address"
    +          },
    +          {
    +            "name": "_interfaceHash",
    +            "type": "bytes32"
    +          }
    +        ],
    +        "name": "getInterfaceImplementer",
    +        "outputs": [
    +          {
    +            "name": "",
    +            "type": "address"
    +          }
    +        ],
    +        "payable": false,
    +        "stateMutability": "view",
    +        "type": "function"
    +      },
    +      {
    +        "constant": true,
    +        "inputs": [
    +          {
    +            "name": "_contract",
    +            "type": "address"
    +          },
    +          {
    +            "name": "_interfaceId",
    +            "type": "bytes4"
    +          }
    +        ],
    +        "name": "implementsERC165InterfaceNoCache",
    +        "outputs": [
    +          {
    +            "name": "",
    +            "type": "bool"
    +          }
    +        ],
    +        "payable": false,
    +        "stateMutability": "view",
    +        "type": "function"
    +      },
    +      {
    +        "constant": true,
    +        "inputs": [
    +          {
    +            "name": "_contract",
    +            "type": "address"
    +          },
    +          {
    +            "name": "_interfaceId",
    +            "type": "bytes4"
    +          }
    +        ],
    +        "name": "implementsERC165Interface",
    +        "outputs": [
    +          {
    +            "name": "",
    +            "type": "bool"
    +          }
    +        ],
    +        "payable": false,
    +        "stateMutability": "view",
    +        "type": "function"
    +      },
    +      {
    +        "anonymous": false,
    +        "inputs": [
    +          {
    +            "indexed": true,
    +            "name": "addr",
    +            "type": "address"
    +          },
    +          {
    +            "indexed": true,
    +            "name": "interfaceHash",
    +            "type": "bytes32"
    +          },
    +          {
    +            "indexed": true,
    +            "name": "implementer",
    +            "type": "address"
    +          }
    +        ],
    +        "name": "InterfaceImplementerSet",
    +        "type": "event"
    +      },
    +      {
    +        "anonymous": false,
    +        "inputs": [
    +          {
    +            "indexed": true,
    +            "name": "addr",
    +            "type": "address"
    +          },
    +          {
    +            "indexed": true,
    +            "name": "newManager",
    +            "type": "address"
    +          }
    +        ],
    +        "name": "ManagerChanged",
    +        "type": "event"
    +      }
    +    ],
    +    "devdoc": {
    +      "author": "Jordi Baylina and Jacques Dafflon",
    +      "methods": {
    +        "getInterfaceImplementer(address,bytes32)": {
    +          "params": {
    +            "_addr": "Address being queried for the implementer of an interface. (If `_addr == 0` then `msg.sender` is assumed.)",
    +            "_interfaceHash": "keccak256 hash of the name of the interface as a string. E.g., `web3.utils.keccak256('ERC777Token')`."
    +          },
    +          "return": "The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0x0` if `_addr` did not register an implementer for this interface."
    +        },
    +        "getManager(address)": {
    +          "params": {
    +            "_addr": "Address for which to return the manager."
    +          },
    +          "return": "Address of the manager for a given address."
    +        },
    +        "implementsERC165Interface(address,bytes4)": {
    +          "details": "This function may modify the state when updating the cache. However, this function must have the `view` modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the ERC165 cache is updated.",
    +          "params": {
    +            "_contract": "Address of the contract to check.",
    +            "_interfaceId": "ERC165 interface to check."
    +          },
    +          "return": "`true` if `_contract` implements `_interfaceId`, false otherwise."
    +        },
    +        "implementsERC165InterfaceNoCache(address,bytes4)": {
    +          "params": {
    +            "_contract": "Address of the contract to check.",
    +            "_interfaceId": "ERC165 interface to check."
    +          },
    +          "return": "`true` if `_contract` implements `_interfaceId`, false otherwise."
    +        },
    +        "interfaceHash(string)": {
    +          "params": {
    +            "_interfaceName": "Name of the interface."
    +          },
    +          "return": "The keccak256 hash of an interface name."
    +        },
    +        "setInterfaceImplementer(address,bytes32,address)": {
    +          "params": {
    +            "_addr": "Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)",
    +            "_interfaceHash": "keccak256 hash of the name of the interface as a string. For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface."
    +          }
    +        },
    +        "setManager(address,address)": {
    +          "params": {
    +            "_addr": "Address for which to set the new manager. (If `_addr == 0` then `msg.sender` is assumed.)",
    +            "_newManager": "Address of the new manager for `addr`. (Pass `0x0` to reset the manager to `_addr` itself.)"
    +          }
    +        },
    +        "updateERC165Cache(address,bytes4)": {
    +          "params": {
    +            "_contract": "Address of the contract for which to update the cache.",
    +            "_interfaceId": "ERC165 interface for which to update the cache."
    +          }
    +        }
    +      },
    +      "title": "ERC820 Pseudo-introspection Registry Contract"
    +    },
    +    "userdoc": {
    +      "methods": {
    +        "getInterfaceImplementer(address,bytes32)": {
    +          "notice": "Query if an address implements an interface and through which contract."
    +        },
    +        "getManager(address)": {
    +          "notice": "Get the manager of an address."
    +        },
    +        "implementsERC165Interface(address,bytes4)": {
    +          "notice": "Checks whether a contract implements an ERC165 interface or not. The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`."
    +        },
    +        "implementsERC165InterfaceNoCache(address,bytes4)": {
    +          "notice": "Checks whether a contract implements an ERC165 interface or not without using nor updating the cache."
    +        },
    +        "interfaceHash(string)": {
    +          "notice": "Compute the keccak256 hash of an interface given its name."
    +        },
    +        "setInterfaceImplementer(address,bytes32,address)": {
    +          "notice": "Sets the contract which implements a specific interface for an address. Only the manager defined for that address can set it. (Each address is the manager for itself until it sets a new manager.)"
    +        },
    +        "setManager(address,address)": {
    +          "notice": "Sets the `_newManager` as manager for the `_addr` address. The new manager will be able to call `setInterfaceImplementer` for `_addr`."
    +        },
    +        "updateERC165Cache(address,bytes4)": {
    +          "notice": "Updates the cache with whether contract implements an ERC165 interface or not."
    +        }
    +      }
    +    }
    +  },
    +  "settings": {
    +    "compilationTarget": {
    +      "./contracts/ERC820Registry.sol": "ERC820Registry"
    +    },
    +    "evmVersion": "byzantium",
    +    "libraries": {},
    +    "optimizer": {
    +      "enabled": true,
    +      "runs": 200
    +    },
    +    "remappings": []
    +  },
    +  "sources": {
    +    "./contracts/ERC820Registry.sol": {
    +      "content": "pragma solidity 0.4.24;\n// IV is a simply value to have a \"nice\" address for the registry, starting with `0x820`.\n// IV: 12302\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some interface for any address other than itself.\ninterface ERC820ImplementerInterface {\n    /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.\n    /// @param addr Address for which the contract will implement the interface\n    /// @param interfaceHash keccak256 hash of the name of the interface\n    /// @return ERC820_ACCEPT_MAGIC only if the contract implements `ìnterfaceHash` for the address `addr`.\n    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32);\n}\n\n/// @title ERC820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820\ncontract ERC820Registry {\n    /// @notice ERC165 Invalid ID.\n    bytes4 constant INVALID_ID = 0xffffffff;\n    /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).\n    bytes4 constant ERC165ID = 0x01ffc9a7;\n    /// @notice Magic value which is returned if a contrct implements an interface on behalf of some other address.\n    bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked(\"ERC820_ACCEPT_MAGIC\"));\n\n    mapping (address => mapping(bytes32 => address)) interfaces;\n    mapping (address => address) managers;\n    mapping (address => mapping(bytes4 => bool)) erc165Cached;\n\n    /// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.\n    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n    /// @notice Indicates `newManager` is the address of the new manager for `addr`.\n    event ManagerChanged(address indexed addr, address indexed newManager);\n\n    /// @notice Query if an address implements an interface and through which contract.\n    /// @param _addr Address being queried for the implementer of an interface.\n    /// (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// E.g., `web3.utils.keccak256('ERC777Token')`.\n    /// @return The address of the contract which implements the interface `_interfaceHash` for `_addr`\n    /// or `0x0` if `_addr` did not register an implementer for this interface.\n    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        if (isERC165Interface(_interfaceHash)) {\n            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0;\n        }\n        return interfaces[addr][_interfaceHash];\n    }\n\n    /// @notice Sets the contract which implements a specific interface for an address.\n    /// Only the manager defined for that address can set it.\n    /// (Each address is the manager for itself until it sets a new manager.)\n    /// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface.\n    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n\n        require(!isERC165Interface(_interfaceHash), \"Must not be a ERC165 hash\");\n        if (_implementer != 0 && _implementer != msg.sender) {\n            require(\n                ERC820ImplementerInterface(_implementer)\n                    .canImplementInterfaceForAddress(addr, _interfaceHash) == ERC820_ACCEPT_MAGIC,\n                \"Does not implement the interface\"\n            );\n        }\n        interfaces[addr][_interfaceHash] = _implementer;\n        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);\n    }\n\n    /// @notice Sets the `_newManager` as manager for the `_addr` address.\n    /// The new manager will be able to call `setInterfaceImplementer` for `_addr`.\n    /// @param _addr Address for which to set the new manager. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _newManager Address of the new manager for `addr`. (Pass `0x0` to reset the manager to `_addr` itself.)\n    function setManager(address _addr, address _newManager) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n        managers[addr] = _newManager == addr ? 0 : _newManager;\n        emit ManagerChanged(addr, _newManager);\n    }\n\n    /// @notice Get the manager of an address.\n    /// @param _addr Address for which to return the manager.\n    /// @return Address of the manager for a given address.\n    function getManager(address _addr) public view returns(address) {\n        // By default the manager of an address is the same address\n        if (managers[_addr] == 0) {\n            return _addr;\n        } else {\n            return managers[_addr];\n        }\n    }\n\n    /// @notice Compute the keccak256 hash of an interface given its name.\n    /// @param _interfaceName Name of the interface.\n    /// @return The keccak256 hash of an interface name.\n    function interfaceHash(string _interfaceName) public pure returns(bytes32) {\n        return keccak256(abi.encodePacked(_interfaceName));\n    }\n\n    /* --- ERC165 Related Functions --- */\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not.\n    /// The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    /// @dev This function may modify the state when updating the cache. However, this function must have the `view`\n    /// modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the ERC165 cache\n    /// is updated.\n    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        if (!erc165Cached[_contract][_interfaceId]) {\n            updateERC165Cache(_contract, _interfaceId);\n        }\n        return interfaces[_contract][_interfaceId] != 0;\n    }\n\n    /// @notice Updates the cache with whether contract implements an ERC165 interface or not.\n    /// @param _contract Address of the contract for which to update the cache.\n    /// @param _interfaceId ERC165 interface for which to update the cache.\n    function updateERC165Cache(address _contract, bytes4 _interfaceId) public {\n        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0;\n        erc165Cached[_contract][_interfaceId] = true;\n    }\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        uint256 success;\n        uint256 result;\n\n        (success, result) = noThrowCall(_contract, ERC165ID);\n        if (success == 0 || result == 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, INVALID_ID);\n        if (success == 0 || result != 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, _interfaceId);\n        if (success == 1 && result == 1) {\n            return true;\n        }\n        return false;\n    }\n\n    /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n    /// @param _interfaceHash The hash to check.\n    /// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise.\n    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n    }\n\n    function noThrowCall(address _contract, bytes4 _interfaceId)\n        internal view returns (uint256 success, uint256 result)\n    {\n        bytes4 erc165ID = ERC165ID;\n\n        assembly {\n                let x := mload(0x40)               // Find empty storage location using \"free memory pointer\"\n                mstore(x, erc165ID)                // Place signature at begining of empty storage\n                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n                success := staticcall(\n                    30000,                         // 30k gas\n                    _contract,                     // To addr\n                    x,                             // Inputs are stored at location x\n                    0x08,                          // Inputs are 8 bytes long\n                    x,                             // Store output over input (saves space)\n                    0x20                           // Outputs are 32 bytes long\n                )\n\n                result := mload(x)                 // Load the result\n        }\n    }\n}\n",
    +      "keccak256": "0xb7b2e57b5db209f6d6c10c9172abe541be3010bfb194e8fadb460fd58608a9ee"
    +    }
    +  },
    +  "version": 1
    +}
    +
    ### Interface name -Your interface name is hashed and sent to `getInterfaceImplementer()`. If you are writing a standard, it is best practice to explicitly state the interface name and link to this published EIP-820 so that other people don't have to come here to look up these rules. +Any interface name is hashed using `keccak256` and sent to `getInterfaceImplementer()`. -#### If it's an approved EIP +If the interface is part of a standard, it is best practice to explicitly state the interface name and link to this published [ERC820] such that other people don't have to come here to look up these rules. -The interface is named like `ERC###XXXXX`. The meaning of this interface is defined in the EIP specified. And XXXXX should be the name of the interface camelCase. +#### **Approved ERCs** + +If the interface is part of an approved ERC, it MUST be named `ERC###XXXXX` where `###` is the number of the ERC and XXXXX should be the name of the interface in CamelCase. The meaning of this interface SHOULD be defined in the specified ERC. Examples: -`sha3("ERC20Token")` -`sha3("ERC777Token")` -`sha3("ERC777TokensRecipient")` -`sha3("ERC777TokensSender")` +- `keccak256("ERC20Token")` +- `keccak256("ERC777Token")` +- `keccak256("ERC777TokensSender")` +- `keccak256("ERC777TokensRecipient")` -#### [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) compatible interfaces +#### **[ERC165] Compatible Interfaces** -Interfaces where the last 28bytes are 0, then this will be considered an [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) interface. +Any interface where the last 28 bytes are zeroes (`0`) SHALL be considered an [ERC165] interface. -#### Private user defined interface +#### **Private User-defined Interfaces** -This scheme is extensible. If you want to make up your own interface name and raise awareness to get other people to implement it and then check for those implementations, great! Have fun, but please do not conflict with the reserved designations above. +This scheme is extensible. You MAY make up your own interface name and raise awareness to get other people to implement it and then check for those implementations. Have fun but please, you MUST not conflict with the reserved designations above. + +### Set An Interface For An Address + +For any address to set a contract as the interface implementation, it must call the following function of the [ERC820] registry: + +``` solidity +function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) public +``` + +Sets the contract that will handle a specific interface. + +Only a `manager` defined for that address can set it. (Each address is the manager for itself until a new manager is defined) + +*NOTE*: If `_addr` and `_implementer` are two different addresses, then: + +- The `_implementer` MUST implement the `ERC820ImplementerInterface` (detailed below). +- Calling `canImplementInterfaceForAddress` on `_implementer` with the given `_addr` and `_interfaceHash` MUST return the `ERC820_ACCEPT_MAGIC` value. + +*NOTE*: The `_interfaceHash` MUST NOT be an [ERC165] interface—it MUST NOT end with 28 zeroes (`0`). + +> **parameters** +> `_addr` Address to define the interface for (if `_addr == 0` them `msg.sender` is assumed) +> `_interfaceHash` `keccak256` hash of the name of the interface as a string, for example `web3.utils.keccak256('ERC777TokensRecipient')` for the ERC777TokensRecipient interface. + +### Get An Implementation Of An Interface For An Address + +Anyone MAY query the [ERC820] Registry to obtain the address of a contract implementing an interface on behalf of some address using the `getInterfaceImplementer` function. + +``` solidity +function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) public view returns (address) +``` + +Query if an address implements an interface and through which contract. + +*NOTE*: If the last 28 bytes of the `_interfaceHash` are zeroes (`0`), then the first 4 bytes are considered an [ERC165] interface and the registry SHALL forward the call to the contract at `_addr` to see if it implements the [ERC165] interface (the first 4 bytes of `_interfaceHash`). The registry SHALL also cache [ERC165] queries in order to reduce gas consumption. Anyone MAY call the `erc165UpdateCache` function to update whether a contract implements an interface or not. + +> **parameters** +> `_addr` Address being queried for the implementer of an interface. (If `_addr == 0` them `msg.sender` is assumed.) +> `_interfaceHash` keccak256 hash of the name of the interface as a string. E.g. `web3.utils.keccak256('ERC777Token')` +> **returns:** The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0x0` if `_addr` did not registeran implemeter for this interface. + + +### Interface Implementation (`ERC820ImplementerInterface`) + +``` solidity +interface ERC820ImplementerInterface { + /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr`. + /// @param addr Address for which the contract will implement the interface + /// @param interfaceHash keccak256 hash of the name of the interface + /// @return ERC820_ACCEPT_MAGIC only if the contract implements `ìnterfaceHash` for the address `addr`. + function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32); +} +``` + +Any contract being registered as the implementation of an interface for a given address MUST implement said interface. In addition if it implements an interface on behalf of a different address, the contract MUST implement the `ERC820ImplementerInterface` shown above. + +``` solidity +function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) view public returns(bytes32); +``` + +Indicates whether a contract implements an interface (`interfaceHash`) for a given address (`addr`). + +If a contract implements the interface (`interfaceHash`) for a given address (`addr`), it MUST return `ERC820_ACCEPT_MAGIC` when called with the `addr` and the `interfaceHash`. If it does not implement the `interfaceHash` for a given address (`addr`), it MUST NOT return `ERC820_ACCEPT_MAGIC`. + +> **parameters** +> `addr`: Address for which the interface is implemented +> `interfaceHash`: Hash of the interface which is implemented + +The special value `ERC820_ACCEPT_MAGIC` is defined as the `keccka256` hash of the string `"ERC820_ACCEPT_MAGIC"`. + +``` solidity +bytes32 constant ERC820_ACCEPT_MAGIC = keccak256("ERC820_ACCEPT_MAGIC"); +``` + +> The reason to return `ERC820_ACCEPT_MAGIC` instead of a boolean is to prevent cases where a contract fails to implement the `canImplementInterfaceForAddress` but implements a fallback function which does not throw. In this case, since `canImplementInterfaceForAddress` does not exist, the fallback function is called instead, executed without throwing and returns `1`. Thus making it appear as if `canImplementInterfaceForAddress` returned `true`. + +### Manager + +The manager of an address (regular account or a contract) is the only entity allowed to register implementations of interfaces for the address. By default any address is its own manager. + +The manager can transfer its role to another address by calling `setManager` with the address for which to transfer the manager and the address of the new manager. + +``` solidity +function setManager(address _addr, address _newManager) public +``` + +Sets the `_newManager` as manager for the `_addr` address. + +The new manager will be able to call `setInterfaceImplementer` for `_addr`. + +If `_addr` is `0x0`, `msg.sender` is assumed for `_addr`. +If `_newManager` is `0x0`, the manager is reset to `_addr` itself as the manager. + +> **parameters** +> `_addr` Address for which to set the new manager. (Pass `0x0` to use `msg.sender` as the address.) +> `_newManager` The address of the new manager for `_addr`. (Pass `0x0` to reset the manager to `_addr`.) ## Backwards Compatibility -This standard is backwards compatible with [EIP-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md), as both methods can be implemented without conflicting one each other. +This standard is backwards compatible with [ERC165], as both methods MAY be implemented without conflicting with each other. ## Test Cases -Please, check the repository https://github.com/jbaylina/eip820 for the full test suit. +Please check the [jbaylina/eip820] repository for the full test suite. ## Implementation -The implementation can be found in this repo: https://github.com/jbaylina/eip820 +The implementation can be found in this repo: [jbaylina/eip820]. ## Copyright -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Copyright and related rights waived via [CC0]. + +[ERC155]: https://eips.ethereum.org/EIPS/eip-155 +[ERC165]: https://eips.ethereum.org/EIPS/eip-165 +[ERC672]: https://github.com/ethereum/EIPs/issues/672 +[ERC820]: https://eips.ethereum.org/EIPS/eip-820 +[ERC820 registry smart contract]: https://github.com/jbaylina/eip820/blob/master/contracts/ERC820Registry.sol +[jbaylina/eip820]: https://github.com/jbaylina/eip820 +[CC0]: https://creativecommons.org/publicdomain/zero/1.0/ +[Nick]: https://github.com/Arachnid/ From 46d1ba6452ad31cade17d3550548a1ca78e8a192 Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Mon, 13 Aug 2018 04:52:05 +0200 Subject: [PATCH 106/177] Automatically merged updates to draft EIP(s) 777 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-777.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/EIPS/eip-777.md b/EIPS/eip-777.md index 68562861..f0fdac84 100644 --- a/EIPS/eip-777.md +++ b/EIPS/eip-777.md @@ -75,7 +75,9 @@ The token contract MUST implement the above interface. The implementation MUST f The token contract MUST register the `ERC777Token` interface with its own address via [ERC820]. If the contract has a switch to enable or disable [ERC777] functions, every time the switch is triggered, the token MUST register or unregister the `ERC777Token` interface for its own address accordingly via [ERC820]. (Unregistering implies setting the address to `0x0`.) -The basic unit token MUST be 1018 (i.e., [ERC20]'s `decimals` MUST be `18`). All functions MUST consider any amount of tokens (such as balances and amount to send, mint, or burn) in the basic unit. +The smallest unit—for all interactions with the token contract—MUST be `1`. I.e. all amounts and balances MUST be unsigned integers. The display denomination—to display any amount to the end user—MUST be 1018 of the smallest. + +In other words the technical denomination is similar to a wei and the display denomination is similar to an ether. It is equivalent to an [ERC20]'s `decimals` function returning `18`. E.g. if a token contract holds a balance of `500,000,000,000,000,000` (0.5×1018) for a user, the user interface SHOULD show `0.5` tokens to the user. If the user wishes to send `0.3` tokens, the contract MUST be called with an amount of `300,000,000,000,000,000` (0.3×1018). #### **View Functions** @@ -111,7 +113,7 @@ Get the total number of minted tokens. The total supply MUST be equal to the sum of the balances of all addresses—as returned by the `balanceOf` function. -The total supply MUST be equal to the sum of all the minted tokens as defined in all the `Minted` events minus the sum of all the burned tokens as defined in all the `Burned` events. +The total supply MUST be equal to the sum of all the minted tokens as defined in all the `Minted` events minus the sum of all the burned tokens as defined in all the `Burned` events. One exception is a cloned token (with cloned balances) where the newer token contract MAY have a `totalSupply` greater than the difference of all the `Minted` and `Burned` events. > **returns:** Total supply of tokens currently in circulation. @@ -138,22 +140,24 @@ function granularity() public view returns (uint256) Get the smallest part of the token that's not divisible. +In other words, the granularity is the smallest number of tokens (in the basic unit) which MAY be minted, sent or burned at any time. + The following rules MUST be applied regarding the *granularity*: -- The *granularity* value MUST be at creation time. +- The *granularity* value MUST be set at creation time. - The *granularity* value MUST NOT be changed ever. - The *granularity* value MUST be greater or equal to `1`. - Any minting, send or burning of tokens MUST be a multiple of the *granularity* value. - Any operation that would result in a balance that's not a multiple of the *granularity* value MUST be considered invalid, and the transaction MUST `revert`. -*NOTE*: Most of the tokens SHOULD be fully partitionable. I.e., this function SHOULD return `1` unless there is a good reason for not allowing any partition of the token. +*NOTE*: Most of the tokens SHOULD be fully partitionable. I.e., this function SHOULD return `1` unless there is a good reason for not allowing any fraction of the token. > **returns:** The smallest non-divisible part of the token. *NOTE*: [`defaultOperators`][defaultOperators] and [`isOperatorFor`][isOperatorFor] are also `view` functions, defined under the [operators] for consistency. *[ERC20] compatibility requirement*: -The decimals of the token MUST always be `18`. For a *pure* ERC777 token the [ERC20] `decimal` function is OPTIONAL, and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of `18` is implied.) For an [ERC20] enabled token, the `decimal` function is REQUIRED and MUST return `18`. +The decimals of the token MUST always be `18`. For a *pure* ERC777 token the [ERC20] `decimal` function is OPTIONAL, and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of `18` is implied.) For an [ERC20] compatible token, the `decimal` function is REQUIRED and MUST return `18`. (In [ERC20], the `decimals` function is OPTIONAL, but the value if the function is not present, is not clearly defined and may be assumed to be `0`. Hence for compatibility reasons, `decimals` MUST be implemented for [ERC20] compatible tokens.) *[ERC20] compatibility requirement*: The `name`, `symbol`, `totalSupply`, and `balanceOf` `view` functions MUST be backward compatible with [ERC20]. @@ -287,7 +291,7 @@ When an *operator* sends an `amount` of tokens from a *token holder* to a *recip - The balance of the *token holder* MUST be greater or equal to the `amount`—such that its resulting balance is greater or equal to zero (`0`) after the send. - The token contract MUST emit a `Sent` event with the correct values as defined in the [`Sent` Event][sent]. - The *operator* MAY communicate any information in the `operatorData`. -- The token contract MUST call the `tokensToSend` hook of the *token holder* if the *token holder* registers an `ERC777TokensRecipient` implementation via [ERC820]. +- The token contract MUST call the `tokensToSend` hook of the *token holder* if the *token holder* registers an `ERC777TokensSender` implementation via [ERC820]. - The token contract MUST call the `tokensReceived` hook of the *recipient* if the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820]. - The `data` and `operatorData` MUST be immutable during the entire send process—hence the same `data` and `operatorData` MUST be used to call both hooks and emit the `Sent` event. @@ -398,6 +402,8 @@ The token contract MUST `revert` when minting in any of the following cases: - The *recipient* is a contract, and it does not implement the `ERC777TokensRecipient` interface via [ERC820]. - The address of the *recipient* is `0x0`. +*NOTE*: The initial token supply at the creation of the token contract MUST be considered as minting for the amount of the initial supply to the address (or addresses) receiving the initial supply. + *[ERC20] compatibility requirement*: While a `Sent` event MUST NOT be emitted when minting, if the token contract is [ERC20] backward compatible, a `Transfer` event with the `from` parameter set to `0x0` SHOULD be emitted as defined in the [ERC20] standard. From 3354ea2b2c50ae4c62dac4f08ac9c8fb529b460e Mon Sep 17 00:00:00 2001 From: yarrumretep Date: Mon, 13 Aug 2018 18:55:59 -0400 Subject: [PATCH 107/177] Automatically merged updates to draft EIP(s) 1167 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1167.md | 246 ++++++++++++++--------------------------------- 1 file changed, 72 insertions(+), 174 deletions(-) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index 29b04692..50be443d 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -13,21 +13,21 @@ created: 2018-06-22 ## Simple Summary -To simply and cheaply clone contract functionality in an immutable way, we propose to standardize on a minimal bytecode implementation which delegates all calls to a known, fixed address. +To simply and cheaply clone contract functionality in an immutable way, this standard specifies a minimal bytecode implementation that delegates all calls to a known, fixed address. ## Abstract -By standardizing on a known minimal bytecode redirect implementation, this standard will allow 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. Specifically, tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run - and can depend on representations about that code (verified source, third-party audits, etc). This implementation forwards all calls and 100% of the gas 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 (for revert with message). +By standardizing on a known minimal bytecode redirect implementation, 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. Specifically, tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run - and can depend on representations about that code (verified source, third-party audits, etc). This implementation forwards all calls and 100% of the gas 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 (for revert with message). ## Motivation -This standard is desireable to allow for use-cases wherein it is desireable to clone exact contract functionality with a minimum of side effects (e.g. memory slot stomping) and with super-cheap deployment of duplicate proxies. +This standard supports use-cases wherein it is desireable to clone exact contract functionality with a minimum of side effects (e.g. memory slot stomping) and with low gas cost deployment of duplicate proxies. ## Specification -The exact bytecode of the standard clone contract is this: `6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd` wherein the bytes at idices 10 - 29 (inclusive) are replaced with the 20 byte address of the master functionality contract. The reference implementation of this is found at the [optionality/clone-factory](https://github.com/optionality/clone-factory) github repo. +The exact bytecode of the standard clone contract is this: `363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3` wherein the bytes at idices 10 - 29 (inclusive) are replaced with the 20 byte address of the master functionality contract. -Detection of clone and redirection is implemented in the clone-factory repo with a contract deployed on both Kovan and Mainnet that detects the presence of a clone and returns the destination address if the interrogated contract is a clone (handles shortened addresses as well). +A reference implementation of this can be found at the [optionality/clone-factory](https://github.com/optionality/clone-factory) github repo. ## Rationale @@ -41,187 +41,85 @@ The goals of this effort have been the following: ## Backwards Compatibility -There are no backwards compatibility issues. +There are no backwards compatibility issues. There may be some systems that are using earlier versions of the proxy contract bytecode. They will not be compliant with this standard. ## Test Cases -We have included some simple test cases in the clone-factory project that demonstrate the function of this contract including the error handling and error message propagation. +Test cases include: +- invocation with no arguments +- invocation with arguments +- invocation with fixed length return values +- invocation with variable length return values +- invocation with revert (confirming reverted payload is transferred) + +Tests for these cases are included in the reference implementation project. ## Implementation -The exact bytecode for deploying the clone instances is `600034603b57603080600f833981f36000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd` with the 20 'be' bytes at offset 26 replaced with the address of the implementation contract. This deployment bytecode results in a deployed contract of `6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd`. +Deployment bytecode is not included in this specification. One approach is defined in the proxy-contract reference implementation. + +### Standard Proxy +The disassembly of the standard deployed proxy contract code (from r2 and edited to include stack visualization) -The disassembly of the full deployment code (from r2, then edited to account for deployment offset changes) ``` - 0x00000000 6000 push1 0x0 - 0x00000002 34 callvalue - 0x00000003 603b push1 0x3b - ,=< 0x00000005 57 jumpi - | 0x00000006 6030 push1 0x30 - | 0x00000008 80 dup1 - | 0x00000009 600f push1 0xf - | 0x0000000b 83 dup4 - | 0x0000000c 39 codecopy - | 0x0000000d 81 dup2 - | 0x0000000e f3 return - | 0x0000000f 6000 push1 0x0 - | 0x00000011 36 calldatasize - | 0x00000012 81 dup2 - | 0x00000013 80 dup1 - | 0x00000014 37 calldatacopy - | 0x00000015 80 dup1 - | 0x00000016 80 dup1 - | 0x00000017 36 calldatasize - | 0x00000018 81 dup2 - | 0x00000019 73bebebebebe. push20 0xbebebebe - | 0x0000002e 5a gas - | 0x0000002f f4 delegatecall - | 0x00000030 3d returndatasize - | 0x00000031 82 dup3 - | 0x00000032 80 dup1 - | 0x00000033 3e returndatacopy - | 0x00000034 15 iszero - | 0x00000035 602c push1 0x2c // note that this offset is post deployment the following jumpi "arrow" on the left was hand edited - ,==< 0x00000037 57 jumpi - :| 0x00000038 3d returndatasize - :| 0x00000039 90 swap1 - :| 0x0000003a f3 return - ``-> 0x0000003b 5b jumpdest - 0x0000003c 3d returndatasize - 0x0000003d 90 swap1 - 0x0000003e fd revert -``` - -Disassembly of only the deployed contract bytecode is (straight from r2): -``` -| 0x00000000 6000 push1 0x0 -| 0x00000002 36 calldatasize -| 0x00000003 81 dup2 -| 0x00000004 80 dup1 -| 0x00000005 37 calldatacopy -| 0x00000006 80 dup1 -| 0x00000007 80 dup1 -| 0x00000008 36 calldatasize -| 0x00000009 81 dup2 -| 0x0000000a 73bebebebebe. push20 0xbebebebe -| 0x0000001f 5a gas -| 0x00000020 f4 delegatecall -| 0x00000021 3d returndatasize -| 0x00000022 82 dup3 -| 0x00000023 80 dup1 -| 0x00000024 3e returndatacopy -| 0x00000025 15 iszero -| 0x00000026 602c push1 0x2c -| ,=< 0x00000028 57 jumpi -| | 0x00000029 3d returndatasize -| | 0x0000002a 90 swap1 -| | 0x0000002b f3 return -| `-> 0x0000002c 5b jumpdest -| 0x0000002d 3d returndatasize -| 0x0000002e 90 swap1 -\ 0x0000002f fd revert -``` - -The typical deployment pattern would be to deploy a Factory contract that can easily create clones. Here is the reference implementation of the clone-factory pattern: -```solidity -contract CloneFactory { - - event CloneCreated(address indexed target, address clone); - - function createClone(address target) internal returns (address result) { - bytes memory clone = hex"600034603b57603080600f833981f36000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd"; - bytes20 targetBytes = bytes20(target); - for (uint i = 0; i < 20; i++) { - clone[26 + i] = targetBytes[i]; - } - assembly { - let len := mload(clone) - let data := add(clone, 0x20) - result := create(0, data, len) - } - } -} -``` - -To utilize the above implementation, you would extend the contract like this: -```solidity -import "./Thing.sol"; -import "../contracts/CloneFactory.sol"; - - -contract ThingFactory is CloneFactory { - - address public libraryAddress; - - event ThingCreated(address newThingAddress, address libraryAddress); - - constructor (address _libraryAddress) public { - libraryAddress = _libraryAddress; - } - - function createThing(string _name, uint _value) public { - address clone = createClone(libraryAddress); - Thing(clone).init(_name, _value); - emit ThingCreated(clone, libraryAddress); - } -} +| 0x00000000 36 calldatasize cds +| 0x00000001 3d returndatasize 0 cds +| 0x00000002 3d returndatasize 0 0 cds +| 0x00000003 37 calldatacopy +| 0x00000004 3d returndatasize 0 +| 0x00000005 3d returndatasize 0 0 +| 0x00000006 3d returndatasize 0 0 0 +| 0x00000007 36 calldatasize cds 0 0 0 +| 0x00000008 3d returndatasize 0 cds 0 0 0 +| 0x00000009 73bebebebebe. push20 0xbebebebe 0xbebe 0 cds 0 0 0 +| 0x0000001e 5a gas gas 0xbebe 0 cds 0 0 0 +| 0x0000001f f4 delegatecall suc 0 +| 0x00000020 3d returndatasize rds suc 0 +| 0x00000021 82 dup3 0 rds suc 0 +| 0x00000022 80 dup1 0 0 rds suc 0 +| 0x00000023 3e returndatacopy suc 0 +| 0x00000024 90 swap1 0 suc +| 0x00000025 3d returndatasize rds 0 suc +| 0x00000026 91 swap2 suc 0 rds +| 0x00000027 602b push1 0x2b 0x2b suc 0 rds +| ,=< 0x00000029 57 jumpi 0 rds +| | 0x0000002a fd revert +| `-> 0x0000002b 5b jumpdest 0 rds +\ 0x0000002c f3 return ``` -IMPORANT NOTE: When implementing, it is important to ensure that the master implementation contract cannot be 'initialized' and that it cannot in any way be selfdestructed. +NOTE: as an effort to reduce gas costs as much as possible, the above bytecode depends on EIP-211 specification that `returndatasize` returns zero prior to any calls within the call-frame. `returndatasize` uses 1 less gas than `dup*`. -Clones can be detected using the following contract -```solidity - -contract ContractProbe { - - function probe(address _addr) public view returns (bool isContract, address forwardedTo) { - bytes memory clone = hex"6000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd"; - uint size; - bytes memory code; - - assembly { //solhint-disable-line - size := extcodesize(_addr) - } - - isContract = size > 0; - forwardedTo = _addr; - - if (size <= 48 && size >= 44) { - bool matches = true; - uint i; - - assembly { //solhint-disable-line - code := mload(0x40) - mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) - mstore(code, size) - extcodecopy(_addr, add(code, 0x20), 0, size) - } - for (i = 0; matches && i < 10; i++) { - matches = code[i] == clone[i]; - } - for (i = 0; matches && i < 17; i++) { - if (i == 8) { - matches = code[code.length - i - 1] == byte(uint(clone[48 - i - 1]) - (48 - size)); - } else { - matches = code[code.length - i - 1] == clone[48 - i - 1]; - } - } - if (code[10] != byte(0x73 - (48 - size))) { - matches = false; - } - uint forwardedToBuffer; - if (matches) { - assembly { //solhint-disable-line - forwardedToBuffer := mload(add(code, 31)) - } - forwardedToBuffer &= (0x1 << 20 * 8) - 1; - forwardedTo = address(forwardedToBuffer >> ((48 - size) * 8)); - } - } - } -} +### Vanity Address Optimization +Proxy deployment can be further optimized by installing the master contract at a vanity contract deployment address with leading zero-bytes. By generating a master contract vanity address that includes Z leading 0 bytes in its address, you can shorten the proxy bytecode by replacing the `push20` opcode with `pushN` (where N is 20 - Z) followed by the N non-zero address bytes. The revert jump address is decremented by Z in this case. Here is an example where Z = 4: ``` -The ContractProbe contract is deployed on Kovan at `0x8b98e65e0e8bce0f71a2a22f3d2666591e4cc857`, Ropsten at `0x75f09888af7c9bdfe15317c411dfb03636179a6d` and on Mainnet at `0x0c953133aa046965b83a3de1215ed4285414537c` +| 0x00000000 36 calldatasize cds +| 0x00000001 3d returndatasize 0 cds +| 0x00000002 3d returndatasize 0 0 cds +| 0x00000003 37 calldatacopy +| 0x00000004 3d returndatasize 0 +| 0x00000005 3d returndatasize 0 0 +| 0x00000006 3d returndatasize 0 0 0 +| 0x00000007 36 calldatasize cds 0 0 0 +| 0x00000008 3d returndatasize 0 cds 0 0 0 +| 0x00000009 6fbebebebebe. push16 0xbebebebe 0xbebe 0 cds 0 0 0 +| 0x0000001a 5a gas gas 0xbebe 0 cds 0 0 0 +| 0x0000001b f4 delegatecall suc 0 +| 0x0000001c 3d returndatasize rds suc 0 +| 0x0000001d 82 dup3 0 rds suc 0 +| 0x0000001e 80 dup1 0 0 rds suc 0 +| 0x0000001f 3e returndatacopy suc 0 +| 0x00000020 90 swap1 0 suc +| 0x00000021 3d returndatasize rds 0 suc +| 0x00000022 91 swap2 suc 0 rds +| 0x00000023 6027 push1 0x27 0x27 suc 0 rds +| ,=< 0x00000025 57 jumpi 0 rds +| | 0x00000026 fd revert +| `-> 0x00000027 5b jumpdest 0 rds +\ 0x00000028 f3 return +``` +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/). From e4eac92a0621ca37dde30e243384b4b5e412016d Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Tue, 14 Aug 2018 10:53:11 -0400 Subject: [PATCH 108/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index ac823f63..dbf7047b 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -35,29 +35,24 @@ IF web3 is undefined ``` START dapp -REQUEST[1] Ethereum provider +REQUEST[1] provider IF user approves - INJECT[2] provider API - NOTIFY[3] dapp + RESPOND[2] with provider CONTINUE dapp IF user rejects IF non-Ethereum environment - NOOP[4] + NOOP[3] ``` #### `[1] REQUEST` Dapps MUST request an Ethereum provider API by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of "ETHEREUM_PROVIDER_REQUEST" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK". -#### `[2] INJECT` +#### `[2] RESPOND` -Ethereum-enabled DOM environments MUST expose an Ethereum provider API as a global `ethereum` variable on the `window` object. +Ethereum-enabled DOM environments MUST respond with an Ethereum provider API by emitting an "ethereumprovider" [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent) on the `window` object. This custom event MUST pass a provider API as an `ethereum` property on its `detail` data object. -#### `[3] NOTIFY` - -Ethereum-enabled DOM environments MUST notify dapps of successful provider API exposure by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of "ETHEREUM_PROVIDER_SUCCESS" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK" - -#### `[4] NOOP` +#### `[3] NOOP` If a user rejects access to the Ethereum provider API on an untrusted site, the site itself MUST NOT be notified in any way; notification of a rejection would allow third-party tools to still identify that a client is Ethereum-enabled despite not being granted access to any provider API. @@ -67,12 +62,10 @@ The following example demonstrates one possible implementation of this strategy ```js window.addEventListener('load', () => { - // Listen for provider injection - window.addEventListener('message', ({ data }) => { - if (data && data.type && data.type === 'ETHEREUM_PROVIDER_SUCCESS') { - // Provider API exposed, continue - const networkVersion = await ethereum.send('net_version', []); - } + // Listen for provider response + window.addEventListener('ethereumprovider', async ({ detail: { ethereum } }) => { + // Provider API exposed, continue + const networkVersion = await ethereum.send('net_version', []); }); // Request provider window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, '*'); @@ -81,7 +74,7 @@ window.addEventListener('load', () => { ## Rationale -The pattern of provider auto-injection followed by the previous generation of Ethereum-enabled DOM environements failed to protect user privacy by allowing untrusted websites to uniquely identify Ethereum users. This proposal establishes a new pattern wherein dapps must request access to an Ethereum provider API. This protocol directly prevents fingerprinting attacks by giving users the ability to reject provider exposure on a given website. +The pattern of provider auto-injection followed by the previous generation of Ethereum-enabled DOM environments failed to protect user privacy by allowing untrusted websites to uniquely identify Ethereum users. This proposal establishes a new pattern wherein dapps must request access to an Ethereum provider API. This protocol directly prevents fingerprinting attacks by giving users the ability to reject provider exposure on a given website. ### Constraints From a14968b64ecaefac79826c8bea194acd4a6e0205 Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Tue, 14 Aug 2018 09:12:04 -0700 Subject: [PATCH 109/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index 25491748..77d7b1a0 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -90,6 +90,32 @@ The rootOwner of a composable is gotten by calling `rootOwnerOf(uint256 _tokenId ERC998ERC721 top-down and bottom-up composables are interoperable with each other. It is possible for a top-down composable to own a bottom-up composable or for a top-down composable to own an ERC721 token that owns a bottom-up token. In any configuration calling `rootOwnerOf(uint256 _tokenID)` on a composable will return the root owner address at the top of the ownership tree. +It is important to get the traversal logic of `rootOwnerOf` right. The logic for `rootOwnerOf` is the same whether or not a composable is bottom-up or top-down or both. +Here is the logic: + +``` +Logic for rootOwnerOf(uint256 _tokenId) + +If the token is a bottom-up composable and has a parent token then call rootOwnerOf for the parent token. + If the call was successful then the returned address is the rootOwner. + Otherwise call rootOwnerOfChild for the parent token. + If the call was successful then the returned address is the rootOwner. + Otherwise get the owner address of the token and that is the rootOwner. +Otherwise call rootOwnerOfChild for the token + If the call was successful then the returned address is the rootOwner. + Otherwise get the owner address of the token and that is the rootOwner. +``` + +Calling `rootOwnerOfChild` for a token means the following logic: +```solidity +// Logic for calling rootOwnerOfChild for a tokenId +address tokenOwner = ownerOf(tokenId); +address childContract = address(this); +bytes32 rootOwner = ERC998ERC721(tokenOwner).rootOwnerOfChild(childContract, tokenId); +``` + +But understand that the real call to `rootOwnerOfChild` should be made with assembly so that the code can check if the call failed and so that the `staticcall` opcode is used to ensure that no state is modified. + Tokens/contracts that implement the above authentication and traversal functionality are "composable aware". ### Composable Transfer Function Parameter Format From abc1fc321caab43a69424274d8ac3064ab9470f9 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Sat, 18 Aug 2018 03:40:29 +0800 Subject: [PATCH 110/177] Automatically merged updates to draft EIP(s) 1087 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1087.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1087.md b/EIPS/eip-1087.md index 0f730dc1..a79a0451 100644 --- a/EIPS/eip-1087.md +++ b/EIPS/eip-1087.md @@ -39,7 +39,7 @@ The following changes are made to the EVM: - At the end of the transaction, for each slot in the dirty map: - If the slot was 0 before the transaction and is 0 now, refund 19800 gas. - If the slot was nonzero before the transaction and its value has not changed, refund 4800 gas. - - If the slot was nonzero before the transaction and is 0 now, refund 10000 gas. + - If the slot was nonzero before the transaction and is 0 now, refund 15000 gas. After these changes, transactions that make only a single change to a storage slot will retain their existing costs. However, contracts that make multiple changes will see significantly reduced costs. Repeating the examples from the Motivation section: From 835e423a589f0105532b04ae86790702bfe6df16 Mon Sep 17 00:00:00 2001 From: Nitika Goel <33280614+nitika-goel@users.noreply.github.com> Date: Sat, 18 Aug 2018 01:11:05 +0530 Subject: [PATCH 111/177] ERC1132 - Extending ERC20 with token locking capability (#1331) --- EIPS/eip-1132.md | 152 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 EIPS/eip-1132.md diff --git a/EIPS/eip-1132.md b/EIPS/eip-1132.md new file mode 100644 index 00000000..25220742 --- /dev/null +++ b/EIPS/eip-1132.md @@ -0,0 +1,152 @@ +--- +eip: 1132 +title: Extending ERC20 with token locking capability +author: nitika-goel +type: Standards Track +category: ERC +status: Draft +created: 2018-06-03 +discussions-to: https://github.com/ethereum/EIPs/issues/1132 +--- + +## Simple Summary + +An extension to the ERC20 standard with methods for time-locking of tokens within a contract. + +## Abstract + +This proposal provides basic functionality to time-lock tokens within an ERC20 smart contract for multiple utilities without the need of transferring tokens to an external escrow smart contract. It also allows fetching balance of locked and transferable tokens. + +Time-locking can also be achieved via staking (#900), but that requires transfer of tokens to an escrow contract / stake manager, resulting in the following six concerns: + +1. additional trust on escrow contract / stake manager +2. additional approval process for token transfer +3. increased ops costs due to gas requirements in transfers +4. tough user experience as the user needs to claim the amount back from external escrows +5. inability for the user to track their true token balance / token activity +6. inability for the user to utilize their locked tokens within the token ecosystem. + +## Motivation + +dApps often require tokens to be time-locked against transfers for letting members 1) adhere to vesting schedules and 2) show skin in the game to comply with the underlying business process. I realized this need while building Nexus Mutual and GovBlocks. + +In [Nexus Mutual](https://nexusmutual.io), claim assessors are required to lock their tokens before passing a vote for claims assessment. This is important as it ensures assessors’ skin in the game. The need here was that once a claim assessor locks his tokens for ‘n’ days, he should be able to cast multiple votes during that period of ‘n’ days, which is not feasible with staking mechanism. There are other scenarios like skills/identity verification or participation in gamified token curated registries where time-locked tokens are required as well. + +In [GovBlocks](https://govblocks.io), I wanted to allow dApps to lock member tokens for governance, while still allowing members to use those locked tokens for other activities within the dApp business. This is also the case with DGX governance model where they’ve proposed quarterly token locking for participation in governance activities of DGX. + +In addition to locking functionality, I have proposed a `Lock()` and `Unlock()` event, just like the `Transfer()` event , to track token lock and unlock status. From token holder’s perspective, it gets tough to manage token holdings if certain tokens are transferred to another account for locking, because whenever `balanceOf()` queries are triggered on token holder’s account – the result does not include locked tokens. A `totalBalanceOf()` function intends to solve this problem. + +The intention with this proposal is to enhance the ERC20 standard with token-locking capability so that dApps can time-lock tokens of the members without having to transfer tokens to an escrow / stake manager and at the same time allow members to use the locked tokens for multiple utilities. + +## Specification + +I’ve extended the ERC20 interface with the following enhancements: + +### Locking of tokens +``` +/** + * @dev Locks a specified amount of tokens against an address, + * for a specified reason and time + * @param _reason The reason to lock tokens + * @param _amount Number of tokens to be locked + * @param _time Lock time in seconds + */ +function lock(bytes32 _reason, uint256 _amount, uint256 _time) public returns (bool) +``` + +### Fetching number of tokens locked under each utility +``` +/** + * @dev Returns tokens locked for a specified address for a + * specified reason + * + * @param _of The address whose tokens are locked + * @param _reason The reason to query the lock tokens for + */ + tokensLocked(address _of, bytes32 _reason) view returns (uint256 amount) +``` + +### Fetching number of tokens locked under each utility at a future timestamp +``` +/** + * @dev Returns tokens locked for a specified address for a + * specified reason at a specific time + * + * @param _of The address whose tokens are locked + * @param _reason The reason to query the lock tokens for + * @param _time The timestamp to query the lock tokens for + */ + function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time) public view returns (uint256 amount) +``` + +### Fetching number of tokens held by an address +``` +/** + * @dev @dev Returns total tokens held by an address (locked + transferable) + * @param _of The address to query the total balance of + */ +function totalBalanceOf(address _of) view returns (uint256 amount) +``` + +### Extending lock period +``` +/** + * @dev Extends lock for a specified reason and time + * @param _reason The reason to lock tokens + * @param _time Lock extension time in seconds + */ + function extendLock(bytes32 _reason, uint256 _time) public returns (bool) +``` + +### Increasing number of tokens locked +``` +/** + * @dev Increase number of tokens locked for a specified reason + * @param _reason The reason to lock tokens + * @param _amount Number of tokens to be increased + */ + function increaseLockAmount(bytes32 _reason, uint256 _amount) public returns (bool) +``` +### Fetching number of unlockable tokens under each utility +``` +/** + * @dev Returns unlockable tokens for a specified address for a specified reason + * @param _of The address to query the the unlockable token count of + * @param _reason The reason to query the unlockable tokens for + */ + function tokensUnlockable(address _of, bytes32 _reason) public view returns (uint256 amount) + ``` +### Fetching number of unlockable tokens +``` +/** + * @dev Gets the unlockable tokens of a specified address + * @param _of The address to query the the unlockable token count of + */ + function getUnlockableTokens(address _of) public view returns (uint256 unlockableTokens) +``` +### Unlocking tokens +``` +/** + * @dev Unlocks the unlockable tokens of a specified address + * @param _of Address of user, claiming back unlockable tokens + */ + function unlock(address _of) public returns (uint256 unlockableTokens) +``` + +### Lock event recorded in the token contract +`event Lock(address indexed _of, uint256 indexed _reason, uint256 _amount, uint256 _validity)` + +### Unlock event recorded in the token contract +`event Unlock(address indexed _of, uint256 indexed _reason, uint256 _amount)` + +## Test Cases + +Test cases are available at [https://github.com/nitika-goel/lockable-token](https://github.com/nitika-goel/lockable-token). + +## Implementation + +- Complete implementation available at https://github.com/nitika-goel/lockable-token +- [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/). From a6a4bfeb13ec778ea52d606555a4e27b7fdbd40f Mon Sep 17 00:00:00 2001 From: benk10 Date: Fri, 17 Aug 2018 22:50:05 +0300 Subject: [PATCH 112/177] Automatically merged updates to draft EIP(s) 1285 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1285.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1285.md b/EIPS/eip-1285.md index f91c3d6e..3522a6cf 100644 --- a/EIPS/eip-1285.md +++ b/EIPS/eip-1285.md @@ -1,7 +1,7 @@ --- eip: 1285 title: Increase Gcallstipend gas in the CALL OPCODE -author: Ben Kaufman +author: Ben Kaufman , Adam Levi discussions-to: https://ethereum-magicians.org/t/eip-1285-increase-gcallstipend-gas-in-the-call-opcode/941 status: Draft type: Standards Track From 0352ed8e3624bd4055f6db724e972e2815f218db Mon Sep 17 00:00:00 2001 From: yarrumretep Date: Sat, 18 Aug 2018 15:50:00 -0400 Subject: [PATCH 113/177] Automatically merged updates to draft EIP(s) 1167 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1167.md | 1 + 1 file changed, 1 insertion(+) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index 50be443d..0c851bf5 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -10,6 +10,7 @@ created: 2018-06-22 --- +### Last Call Comment Period Ends August 27, 2018 @ 11PM UTC ## Simple Summary From 5d5db7bc669694b5e94ebf1dfd5561e0534834a5 Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Mon, 20 Aug 2018 14:09:38 -0700 Subject: [PATCH 114/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index 77d7b1a0..ff9ca556 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -37,6 +37,13 @@ With composable tokens it is possible to compose lists or trees of ERC721 and ER Different composables, top-down and bottom-up, have their advantages and disadvantages which are explained in the [Rational section](#rationale). It is possible for a token to be one or more kinds of composable token. +A non-fungible token is ERC998 compliant and is an ERC998 Composable if it implements one or more of the following interfaces: + +* ERC998ERC721TopDown +* ERC998ERC20TopDown +* ERC998ERC721BottomUp +* ERC998ERC20BottomUp + ## Specification ### ERC721 @@ -1307,6 +1314,8 @@ Here is an example of what could occur if **from** was not explicitly provided i ### Additional Reading Material [Top-Down and Bottom-Up Composables, What's the Difference and Which One Should You Use?](https://hackernoon.com/top-down-and-bottom-up-composables-whats-the-difference-and-which-one-should-you-use-db939f6acf1d) +Join the discussion about composables in the [NFTy Magician's Discord](https://discord.gg/8cuuj2Y). + ## Backwards Compatibility Composables are designed to work with ERC721, ERC223 and ERC20 tokens. From 5ace6aa0f43f4ef95a3b12c9391fb2f36dc76a8e Mon Sep 17 00:00:00 2001 From: Witek Date: Tue, 21 Aug 2018 15:35:54 -0700 Subject: [PATCH 115/177] Automatically merged updates to draft EIP(s) 1155 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1155.md | 426 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 326 insertions(+), 100 deletions(-) diff --git a/EIPS/eip-1155.md b/EIPS/eip-1155.md index bee5cffe..1346e91a 100644 --- a/EIPS/eip-1155.md +++ b/EIPS/eip-1155.md @@ -15,9 +15,9 @@ A standard interface for multiple item/token definitions in a single deployed co ## Abstract -This standard proposes a new monolithic token contract that can mint any quantity of Fungible and Non-Fungible tokens in the same contract. We call these "Items" as they differ from the existing standards by being full definitions and configurations of multiple tokens inside a single contract. Standards like ERC-20 require deployment of separate contracts per token. The ERC-721 standard's Token ID is a single non-fungible index and the group of these non-fungibles is deployed as a single contract with settings for the entire collection. Instead, the Crypto Item Standard allows for each Item ID to represent a new configurable token type, which may have its own totalSupply value and other such attributes. +This standard outlines a smart contract interface where one can represent any number of Fungible and Non-Fungible assets in a single contract. Existing standards such as ERC-20 require deployment of separate contracts per token. The ERC-721 standard's Token ID is a single non-fungible index and the group of these non-fungibles is deployed as a single contract with settings for the entire collection. Instead, the ERC-1155 Crypto Item Standard allows for each Item ID to represent a new configurable token type, which may have its own totalSupply value and other such attributes. -The `_itemId` parameter is named as such and placed at the beginning of each function. +The `_id` parameter is contained in each function's parameters and indicates a specific item or item type in a transaction. ## Motivation @@ -27,126 +27,305 @@ New functionality is possible with this design, such as transferring or approvin ## Specification -``` -solidity -interface ICryptoItems { - // Events - event Transfer(uint256 indexed _itemId, address indexed _from, address indexed _to, uint256 _value); - event Approval(uint256 indexed _itemId, address indexed _owner, address indexed _spender, uint256 _value); +```solidity +pragma solidity ^0.4.24; - // Required Functions - function transfer(uint256[] _itemId, address[] _to, uint256[] _value) external returns (bool success); - function transferFrom(uint256[] _itemId, address[] _from, address[] _to, uint256[] _value) external returns (bool success); - function approve(uint256[] _itemId, address[] _spender, uint256[] _value) external returns (bool success); - function increaseApproval(uint256[] _itemId, address[] _spender, uint256[] _addedValue) external returns (bool success); - function decreaseApproval(uint256[] _itemId, address[] _spender, uint256[] _subtractedValue) external returns (bool success); +/** + @title ERC-1155 Crypto Items Standard + @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md + Note: the ERC-165 identifier for this interface is 0xf23a6e61. + */ +interface IERC1155 { + /** + @dev MUST trigger on any successful call to approve(address _spender, uint256 _value) + */ + event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _oldValue, uint256 _value); + + /** + @dev MUST trigger when tokens are transferred, including zero value transfers + 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 _spender, address indexed _from, address indexed _to, uint256 indexed _id, uint256 _value); - // Required View Functions - function totalSupply(uint256 _itemId) external view returns (uint256); - function balanceOf(uint256 _itemId, address _owner) external view returns (uint256); - function allowance(uint256 _itemId, address _owner, address _spender) external view returns (uint256); - - // Optional View Functions - function name(uint256 _itemId) external view returns (string); - function symbol(uint256 _itemId) external view returns (string); - function decimals(uint256 _itemId) external view returns (uint8); - - // Optional Functions for Non-Fungible Items - function ownerOf(uint256 _itemId) external view returns (address); - function itemURI(uint256 _itemId) external view returns (string); - function itemByIndex(uint256 _itemId, uint256 _index) external view returns (uint256); - function itemOfOwnerByIndex(uint256 _itemId, address _owner, uint256 _index) external view returns (uint256); + /** + @dev Transfers value amount of an _id from the _from address to the _to addresses specified. Each parameter array should be the same length, with each index correlating. + Caller must have a sufficient allowance by _from for the _id/_value pair + @param _from source addresses + @param _to target addresses + @param _id ID of the CryptoItem + @param _value transfer amounts + */ + function transferFrom(address _from, address _to, uint256 _id, uint256 _value) external; + + /** + @dev Transfers value amount of an _id from the _from address to the _to addresses specified. Each parameter array should be the same length, with each index correlating. + Caller must have a sufficient allowance by _from for the _id/_value pair + Throws if `_to` is the zero address. + Throws if `_id` is not a valid NFT. + When transfer is complete, this function checks if `_to` is a smart contract (code size > 0). If so, it calls `onERC1155Received` on `_to` and throws if the return value is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`. + @param _from source addresses + @param _to target addresses + @param _id ID of the CryptoItem + @param _value transfer amounts + @param _data Additional data with no specified format, sent in call to `_to` + */ + function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes _data) external; + + /** + @dev Allow other accounts/contracts to spend tokens on behalf of msg.sender + Also, to minimize the risk of the approve/transferFrom attack vector + (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), this function will throw if the current approved allowance does not equal the expected _currentValue, unless _value is 0 + @param _spender Address to approve + @param _id ID of the CryptoItem + @param _currentValue Expected current value of approved allowance. + @param _value Allowance amount + */ + function approve(address _spender, uint256 _id, uint256 _currentValue, uint256 _value) external; + + /** + @dev Get the balance of an account's CryptoItems + @param _id ID of the CryptoItem + @param _owner The address of the token holder + @return The _owner's balance of the CryptoItem type requested + */ + function balanceOf(uint256 _id, address _owner) external view returns (uint256); + + /** + @dev Queries the spending limit approved for an account + @param _id ID of the CryptoItem + @param _owner The owner allowing the spending + @param _spender The address allowed to spend. + @return The _spender's allowed spending balance of the CryptoItem requested + */ + function allowance(uint256 _id, address _owner, address _spender) external view returns (uint256); } ``` -### transfer +
    + +Simple/Extended Transfers -Transfers quantities of each `_itemId[]` to the addresses specified. -Each parameter array should be the same length, with each index correlating. +```solidity +interface IERC1155Extended { + /** + @dev Send a single type of CryptoItem + @param _to Transfer destination address + @param _id ID of the CryptoItem + @param _value Transfer amount + */ + function transfer(address _to, uint256 _id, uint256 _value) external; + + /** + @dev Send a single type of CryptoItem (with safety call) + @param _to Transfer destination address + @param _id ID of the CryptoItem + @param _value Transfer amount + @param _data Additional data with no specified format, sent in call to `_to` + */ + function safeTransfer(address _to, uint256 _id, uint256 _value, bytes _data) external; +} +``` -MUST trigger `Transfer` event. +
    -### transferFrom +
    + +Batch Transfers -Transfers quantities of each `_itemId[]` from one or more `_from[]` addresses to the `_to[]` addresses specified. -Each parameter array should be the same length, with each index correlating. +```solidity +interface IERC1155BatchTransfer { + /** + @dev Send multiple types of CryptoItems from a 3rd party in one transfer + For the purpose of transfer fees, each id/value pair is counted as a transfer + Caller must have a sufficient allowance by _from for each of the _id/_value pairs + Throws on any error rather than return a false flag to minimize user errors + @param _from Source address + @param _to Target address + @param _ids Types of CryptoItems + @param _values Transfer amounts per item type + */ + function batchTransferFrom(address _from, address _to, uint256[] _ids, uint256[] _values) external; + + /** + @dev Send multiple types of CryptoItems from a 3rd party in one transfer (with safety call) + For the purpose of transfer fees, each id/value pair is counted as a transfer + Caller must have a sufficient allowance by _from for each of the _id/_value pairs + Throws on any error rather than return a false flag to minimize user errors + @param _from Source address + @param _to Target address + @param _ids Types of CryptoItems + @param _values Transfer amounts per item type + @param _data Additional data with no specified format, sent in call to `_to` + */ + function safeBatchTransferFrom(address _from, address _to, uint256[] _ids, uint256[] _values, bytes _data) external; + + /** + @dev Allow other accounts/contracts to spend tokens on behalf of msg.sender + Also, to minimize the risk of the approve/transferFrom attack vector + (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), this function will throw if the current approved allowance does not equal the expected _currentValue, unless _value is 0 + @param _spender Address to approve + @param _ids IDs of the CryptoItems + @param _currentValues Expected current values of allowances per item type + @param _values Allowance amounts per item type + */ + function batchApprove(address _spender, uint256[] _ids, uint256[] _currentValues, uint256[] _values) external; +} +``` -MUST trigger `Transfer` event. +
    -### approve +
    + +Simple/Extended Batch Transfers -Approves an account for the ability to transfer a maximum quantity of multiple `_itemId[]` on behalf of another account (using transferFrom). -Each parameter array should be the same length, with each index correlating. +```solidity +interface IERC1155BatchTransferExtended { + /** + @dev Send multiple types of CryptoItems in one transfer + For the purpose of transfer fees, each id/value pair is counted as a transfer + @param _to Transfer destination address + @param _ids IDs of the CryptoItems + @param _values Transfer amounts per item type + */ + function batchTransfer(address _to, uint256[] _ids, uint256[] _values) external; + + /** + @dev Send multiple types of CryptoItems in one transfer (with safety call) + For the purpose of transfer fees, each id/value pair is counted as a transfer + @param _to Transfer destination address + @param _ids IDs of the CryptoItems + @param _values Transfer amounts per item type + @param _data Additional data with no specified format, sent in call to `_to` + */ + function safeBatchTransfer(address _to, uint256[] _ids, uint256[] _values, bytes _data) external; +} +``` -MUST trigger `Approval` event. +
    -### increaseApproval +
    + +Extended Approvals -Increases the allowance amount of one or more items without requiring a reset to 0. -Each parameter array should be the same length, with each index correlating. +```solidity +interface IERC1155Operators { + event OperatorApproval(address indexed _owner, address indexed _operator, uint256 indexed _id, bool _approved); + event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); -MUST trigger `Approval` event. + /** + @notice Enable or disable approval for a third party ("operator") to manage + all of `msg.sender`'s assets for a particular CryptoItem types. + @dev Emits the OperatorApproval event + @param _operator Address to add to the set of authorized operators + @param _ids The IDs of the CryptoItems + @param _approved True if the operators is approved, false to revoke approval + */ + function setApproval(address _operator, uint256[] _ids, bool _approved) external; + + /** + @dev Queries the approval status of an operator for a given CryptoItem and owner + @param _owner The owner of the CryptoItems + @param _operator Address of authorized operator + @param _id ID of the CryptoItem + @return True if the operator is approved, false if not + */ + function isApproved(address _owner, address _operator, uint256 _id) external view returns (bool); + + /** + @notice Enable or disable approval for a third party ("operator") to manage + all of `msg.sender`'s assets. + @dev Emits the OperatorApproval event + @param _operator Address to add to the set of authorized operators + @param _approved True if the operator is approved, false to revoke approval + */ + function setApprovalForAll(address _operator, bool _approved) external; + + /** + @dev Queries the approval status of an operator for a given CryptoItem and owner + @param _owner The owner of the CryptoItems + @param _operator Address of authorized operator + @return True if the operator is approved, false if not + */ + function isApprovedForAll(address _owner, address _operator) external view returns (bool isOperator); +} +``` -### decreaseApproval +
    -Decreases the allowance amount of one or more items without requiring a reset to 0. -Each parameter array should be the same length, with each index correlating. +
    + +Views -MUST trigger `Approval` event. +```solidity +interface IERC1155Views { + /** + @dev Returns how many of a CryptoItem are deemed or expected to exist in circulation + @param _id ID of the CryptoItem + @return The number of CryptoItems in circulation + */ + function totalSupply(uint256 _id) external view returns (uint256); + + /** + @dev Returns a human readable string that identifies a CryptoItem, similar to ERC20 + @param _id ID of the CryptoItem + @return The name of the CryptoItem type + */ + function name(uint256 _id) external view returns (string); + + /** + @dev Returns symbol of a CryptoItem, similar to ERC20 and ERC721 + @param _id ID of the CryptoItem + @return The symbol of the CryptoItem + */ + function symbol(uint256 _id) external view returns (string); + + /** + @dev Returns the number of decimal places for a CryptoItem, similar to ERC20 + @param _id ID of the CryptoItem + @return Number of decimals + */ + function decimals(uint256 _id) external view returns (uint8); + + /** + @notice A distinct Uniform Resource Identifier (URI) for a given asset + @dev URIs are defined in RFC 3986 + @return URI string + */ + function uri(uint256 _id) external view returns (string); +} +``` -### name +
    -Returns the Item ID's name. +
    + +ERC-1155 Token Receiver -This function is OPTIONAL but highly recommended. +```solidity +interface IERC1155TokenReceiver { + /** + @notice Handle the receipt of an ERC1155 type + @dev The smart contract calls this function on the recipient + after a `safeTransfer`. This function MAY throw to revert and reject the + transfer. Return of other than the magic value MUST result in the + transaction being reverted + Note: the contract address is always the message sender + @param _operator The address which called `safeTransferFrom` function + @param _from The address which previously owned the token + @param _id The identifier of the item being transferred + @param _value The amount of the item being transferred + @param _data Additional data with no specified format + @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` + */ + function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes _data) external returns(bytes4); +} +``` -### symbol - -Returns the Item ID's symbol. - -This function is OPTIONAL. - -### decimals - -Returns the Item ID's decimals. - -This function is OPTIONAL but highly recommended. - -### totalSupply - -Returns the Item ID's totalSupply. - -### balanceOf - -Returns an account's balance of specified Item ID. - -### allowance - -Returns the allowance set by any of the approve functions. - -### ownerOf - -For NFTs, this returns the owner of a specific `_itemId`. - -This function is OPTIONAL. - -### itemURI - -Returns a distinct Uniform Resource Identifier (URI) for a given `_itemId`. - -This function is OPTIONAL. - -### itemByIndex - -Enumerate valid NFTs. - -This function is OPTIONAL. - -### itemOfOwnerByIndex - -Enumerate NFTs assigned to an owner. - -This function is OPTIONAL. +
    ## Non-Fungible Items @@ -156,9 +335,55 @@ Non-Fungible Items can be interacted with using an index based accessor into the Inside the contract code the two pieces of data needed to access the individual NFT can be extracted with uint128(~0) and the same mask shifted by 128. +### Interface + +
    + +Optional Non Fungible Interface + +```solidity +interface IERC1155NonFungible { + /** + @notice Find the owner of an NFT + @dev NFTs assigned to zero address are considered invalid, and queries about them do throw + @param _id The identifier for an NFT + @return The address of the owner of the NFT + */ + function ownerOf(uint256 _id) external view returns (address); + + /** + @notice Enumerate valid NFs + @dev Throws if `_index` >= `totalSupply()`. + @param _index A counter less than `totalSupply()` + @return The token identifier for the `_index`th NFT (sort order not specified) + */ + function nonFungibleByIndex(uint256 _id, uint128 _index) external view returns (uint256); + + /** + @notice Enumerate NFTs assigned to an owner + @dev Throws if `_index` >= `balanceOf(_owner)` or if + `_owner` is the zero address, representing invalid NFTs + @param _owner An address where we are interested in NFTs owned by them + @param _index A counter less than `balanceOf(_owner)` + @return The token identifier for the `_index`th NFT assigned to `_owner` (sort order not specified) + */ + function nonFungibleOfOwnerByIndex(uint256 _id, address _owner, uint128 _index) external view returns (uint256); + + /** + @notice Is this token non fungible? + @dev If this returns true, the token is non-fungible. + @param _id The identifier for a potential non-fungible. + @return True if the token is non-fungible + */ + function isNonFungible(uint256 _id) external view returns (bool); +} +``` + +
    + ### Example of split ID bits -``` +```solidity uint256 baseToken = 12345 << 128; uint128 index = 50; @@ -168,6 +393,7 @@ balanceOf(baseToken + index, msg.sender); // Get balance of the Non-Fungible tok ## Implementation +- [ERC-1155 Reference Implementation](https://github.com/enjin/erc-1155) - [Enjin Coin](https://enjincoin.io) ([github](https://github.com/enjin)) ## Copyright From 7df9314343d75042b300e3d51f8908ead333d8d3 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Thu, 23 Aug 2018 11:52:59 +0200 Subject: [PATCH 116/177] Automatically merged updates to draft EIP(s) 1109 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1109.md | 60 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/EIPS/eip-1109.md b/EIPS/eip-1109.md index c8c69e45..1d80f910 100644 --- a/EIPS/eip-1109.md +++ b/EIPS/eip-1109.md @@ -1,6 +1,6 @@ --- eip: 1109 -title: Remove CALL costs for precompiled contracts +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 @@ -11,45 +11,73 @@ created: 2018-05-22 ## Simple Summary -This EIP removes the gas costs of the CALL-like opcodes when calling precompiled contracts. +This EIP creates a specific OPCODE named "PRECOMPILEDCALL" to call Precompiled contracts without the costs of a normal CALL. ## Abstract -This EIP tries to resolve the problem of high gas consumption when calling precompiled contracts with a small gas cost. Setting the gas costs to 0 when calling a precompiled contract allows to define precompiled contracts whose effective cost when calling it is less than 700. +This EIP tries to resolve the problem of high gas consumption when calling precompiled contracts with a small gas cost. Using this opcode for calling precompiled contracts allows to define precompiled contracts whose effective cost it is less than 700. ## Motivation -Each precompiled contract has an already defined a cost for calling it. It does not make sense to add the implicit extra gas cost of the CALL opcode. +Each precompiled contract has an already defined cost for calling it. It does not make sense to add the implicit extra gas cost of the CALL opcode. As an example, SHA256 precompiled contract costs 60 and ECADD costs 500 (proposed to costs only 50 in [EIP1108](https://github.com/ethereum/EIPs/pull/1108) . When a precompiled contract is called, 700 gas is consumed just for the CALL opcode besides the costs of the precompiled contract. -This makes no sense, and right now it's impossible to define a precompiled contract whose effective cost for using it is less than 700. +This makes no sense, and right now it's impossible to define a precompiled contract whose effective cost for using it, is less than 700. ## Specification -If `block.number >= XXXXX`, for each CALL, DELEGATECALL and CALLCODE opcode with a destination address < 256 use a Gcall (basic cost of the call) of 0 instead of 700. +If `block.number >= XXXXX`, define a new opcode named PRECOMPILEDCALL with code value 0xfb . + +The gas cost of the OPCODE is 2 (Gbase) plus the Specific gas cost defined for each specific precompiled smart contract. + +The OPCODE takes 5 words from the stack and returns 1 word to the stack. + +The input stack values are: + +mu_s[0] = The address of the precompiled smart contract that is called. +mu_s[1] = Pointer to memory for the input parameters. +mu_s[2] = Length of the input parametes in bytes. +mu_s[3] = Pointer to memory where the output is stored +mu_s[4] = Length of the output buffer. + + +The return will be 1 in case of success call and 0 in any of the next cases: + +1.- mu_s[0] is an address of an undefined precompiled smart contract. +2.- The precompiled smart contract fails (as defined on each smart contract). Invalid input parameters for example. + +Precompiled smart contracs, does not execute opcodes, so there is no need to pass a gas parameter as a normal CALL. If the available gas is less that 2 plus the required gas required for the specific precompiled smart cotract, the context just STOPS executing with an "Out of Gas" error. + +There is no stack check for this call. + +The normal CALLs to the precompiled smart contracts continue to work with the exact same behavior. + +A PRECOMPILEDCALL to a regular address or regular smart contract, is considered a call to an "undefined smart contract", so the VM MUST not execute it and the opcode must return 0x0 . -The cost of the CALL opcode is calculated in exactly the same way that any normal call, including the cost for using a value different than 0 and for creating a new account. The only difference is that Gcall is not added when calculating Cextra. ## Rationale -This implementation is the one that needs less modification of the clients. +There was a first proposal for removing the gast consts for the CALL, but it looks that it's easier to implement and test a new opcode just for that. + +The code is just the next opcode available after the STATICCALL opcode. ## Backwards Compatibility -This EIP is backwards compatible. Smart contracts that call precompiled contracts will cost less from now on. Smart contracts that relay in a high gas consumption for a specific code are not expected. +This EIP is backwards compatible. Smart contracts that call precompiled contracts using this new opcode will cost less from now on. + +Old contracts that call precompiled smart contracts with the CALL method, will continue working. ## Test Cases - Normal call to a defined precompiled contract. - Call to undefined precompiled contract. -- Call to defined precompiled contract with a value!=0 on the call. -- Call to undefined precompiled contract with a value!=0 on the call. -- Normal call to precompiled contract with remaining gas<700 but gas>[the cost of the call]. -- Call to a precompiled contract which forwards all gas but - * 600 - * 700 - * 701 +- Call to a regular contract +- Call to a regular account +- Call to 0x0 smart contract (Does not exists). +- Call with large values for the offste pointers and lenghts +- Call with the exact gas remaining needed to call smart contract. +- Call with the exact gas remaining minus one needed to call smart contract. ## Implementation From 787965eaae5a44c5eb39bb191b399593cb55e0eb Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 23 Aug 2018 21:02:19 +0800 Subject: [PATCH 117/177] Automatically merged updates to draft EIP(s) 1283 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1283.md | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md index add77e1b..14c42626 100644 --- a/EIPS/eip-1283.md +++ b/EIPS/eip-1283.md @@ -191,11 +191,31 @@ anticipated, and many contract will see gas reduction. ## Test Cases -To be added. +Below we provide 17 test cases. 15 of them covering consecutive two +`SSTORE` operations are based on work [by +@chfast](https://github.com/ethereum/tests/issues/483). Two additional +case with three `SSTORE` operations is used to test the case when a +slot is reset and then set again. -## Implementation - -To be added. +| Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | +|------------------------------------|----------|--------|----------|-----|-----|-----| +| `0x60006000556000600055` | 412 | 0 | 0 | 0 | 0 | | +| `0x60006000556001600055` | 20212 | 0 | 0 | 0 | 1 | | +| `0x60016000556000600055` | 20212 | 19800 | 0 | 1 | 0 | | +| `0x60016000556002600055` | 20212 | 0 | 0 | 1 | 2 | | +| `0x60016000556001600055` | 20212 | 0 | 0 | 1 | 1 | | +| `0x60006000556000600055` | 5212 | 15000 | 1 | 0 | 0 | | +| `0x60006000556001600055` | 5212 | 4800 | 1 | 0 | 1 | | +| `0x60006000556002600055` | 5212 | 0 | 1 | 0 | 2 | | +| `0x60026000556000600055` | 5212 | 15000 | 1 | 2 | 0 | | +| `0x60026000556003600055` | 5212 | 0 | 1 | 2 | 3 | | +| `0x60026000556001600055` | 5212 | 4800 | 1 | 2 | 1 | | +| `0x60026000556002600055` | 5212 | 0 | 1 | 2 | 2 | | +| `0x60016000556000600055` | 5212 | 15000 | 1 | 1 | 0 | | +| `0x60016000556002600055` | 5212 | 0 | 1 | 1 | 2 | | +| `0x60016000556001600055` | 412 | 0 | 1 | 1 | 1 | | +| `0x600160005560006000556001600055` | 40218 | 19800 | 0 | 1 | 0 | 1 | +| `0x600060005560016000556000600055` | 10218 | 19800 | 1 | 0 | 1 | 0 | ## Copyright From 445d7d4438c73be2baa0ac2879d59198ca332388 Mon Sep 17 00:00:00 2001 From: Nick Mudge Date: Fri, 24 Aug 2018 06:36:19 -0700 Subject: [PATCH 118/177] Automatically merged updates to draft EIP(s) 998 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-998.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md index ff9ca556..1f8bab05 100644 --- a/EIPS/eip-998.md +++ b/EIPS/eip-998.md @@ -854,7 +854,7 @@ interface ERC998ERC721BottomUp { /// @notice Get the root owner of tokenId. /// @param _tokenId The token to query for a root owner address /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. - function rootOwnerOf(uint256 _tokenId) external view returns (bytes rootOwner); + function rootOwnerOf(uint256 _tokenId) external view returns (bytes32 rootOwner); /// @notice Get the owner addess and parent token (if there is one) of a token /// @param _tokenId The tokenId to query. From 0469efa8376f6e1615fa9771a31c80a991e3399d Mon Sep 17 00:00:00 2001 From: c-g-e-w-e-k-e- Date: Tue, 28 Aug 2018 04:35:55 -0700 Subject: [PATCH 119/177] ERC 1319 Smart Contract Package Registry Interface (#1320) * ERC 1319 * Make generateReleaseId public view --- EIPS/eip-1319.md | 160 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 EIPS/eip-1319.md diff --git a/EIPS/eip-1319.md b/EIPS/eip-1319.md new file mode 100644 index 00000000..1770712d --- /dev/null +++ b/EIPS/eip-1319.md @@ -0,0 +1,160 @@ +--- +eip: 1319 +title: Smart Contract Package Registry Interface +author: Piper Merriam , Christopher Gewecke , g. nicholas d'andrea +type: Standards Track +category: ERC +status: Draft +created: 2018-08-13 +discussions-to: https://github.com/ethereum/EIPs/issues/1319 +--- + +## Simple Summary +A standard interface for smart contract package registries. + +## Abstract +This EIP specifies an interface for publishing to and retrieving assets from smart contract package registries. It is a companion EIP to [1123](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1123.md) which defines a standard for smart contract package manifests. + +## Motivation +The goal is to establish a framework that allows smart contract publishers to design and deploy code registries with arbitrary business logic while exposing a set of common endpoints that tooling can use to retrieve assets for contract consumers. + +A clear standard would help the existing EthPM Package Registry evolve from a centralized, single-project community resource into a decentralized multi-registry system whose constituents are bound together by the proposed interface. In turn, these registries could be ENS name-spaced, enabling installation conventions familiar to users of `npm` and other package managers. + +**Examples** +```shell +$ ethpm install packages.zeppelin.eth/Ownership +``` + +```javascript +const SimpleToken = await web3.packaging + .registry('packages.ethpm.eth') + .getPackage('simple-token') + .getVersion('^1.1.5'); +``` + +## Specification +The specification describes a small read/write API whose components are mandatory. It allows registries to manage versioned releases using the conventions of [semver](https://semver.org/) without imposing this as a requirement. It assumes registries will share the following structure and conventions: + ++ a **registry** is a deployed contract which manages a collection of **packages**. ++ a **package** is a collection of **releases** ++ a **package** is identified by a unique string name and unique bytes32 **packageId** within a given **registry** ++ a **release** is identified by a `bytes32` **releaseId** which must be unique for a given package name and release version string pair. ++ a **releaseId** maps to a set of data that includes a **manifestURI** string which describes the location of an [EIP 1123 package manifest](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1123.md). This manifest contains data about the release including the location of its component code assets. ++ a **manifestURI** is a URI as defined by [RFC3986](https://tools.ietf.org/html/rfc3986) which can be used to retrieve the contents of the package manifest. In addition to validation against RFC3986, each **manifestURI** must also contain a hash of the content as specified in the [EIP 1123](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1123.md). + +### Examples + +**Package Names / Release Versions** + +```shell +"simple-token" # package name +"1.0.1" # version string +``` + +**Release IDs** + +Implementations are free to choose any scheme for generating a **releaseId**. A common approach would be to hash the strings together as below: + +```solidity +// Hashes package name and a release version string +function generateReleaseId(string packageName, string version) + public + view + returns (bytes32) + { + return keccak256(abi.encodePacked(packageName, version)); + } +``` +Implementations **must** expose this id generation logic as part of their public `read` API so +tooling can easily map a string based release query to the registry's unique identifier for that release. + +**Manifest URIs** + +Any IPFS or Swarm URI meets the definition of **manifestURI**. + +Another example is content on GitHub addressed by its SHA-1 hash. The Base64 encoded content at this hash can be obtained by running: +```shell +$ curl https://api.github.com/repos/:owner/:repo/git/blobs/:file_sha + +# Example +$ curl https://api.github.com/repos/rstallman/hello/git/blobs/ce013625030ba8dba906f756967f9e9ca394464a +``` + +The string "hello" can have its GitHub SHA-1 hash independently verified by comparing it to the output of: +```shell +$ printf "blob 6\000hello\n" | sha1sum +> ce013625030ba8dba906f756967f9e9ca394464a +``` + +### Write API Specification +The write API consists of a single method, `release`. It passes the registry the package name, a +version identifier for the release, and a URI specifying the location of a manifest which +details the contents of the release. +```solidity +function release(string packageName, string version, string manifestURI) public + returns (bytes32 releaseId); +``` +### Read API Specification + +The read API consists of a set of methods that allows tooling to extract all consumable data from a registry. + +```solidity +// Retrieves a slice of the list of all unique package identifiers in a registry. +// `offset` and `limit` enable paginated responses / retrieval of the complete set. (See note below) +function getAllPackageIds(uint offset, uint limit) public view + returns ( + bytes32 packageIds, + uint offset + ); + +// Retrieves the unique string `name` associated with a package's id. +function getPackageName(bytes32 packageId) public view returns (string name); + +// Retrieves the registry's unique identifier for an existing release of a package. +function getReleaseId(string packageName, string version) public view returns (bytes32); + +// Retrieves a slice of the list of all release ids for a package. +// `offset` and `limit` enable paginated responses / retrieval of the complete set. (See note below) +function getAllReleaseIds(string packageName, uint offset, uint limit) public view + returns ( + bytes32[] ids, + uint offset + ); + +// Retrieves package name, release version and URI location data for a release id. +function getReleaseData(bytes32 releaseId) public view + returns ( + string packageName, + string version, + string manifestURI + ); + +// Retrieves the release id a registry *would* generate for a package name and version pair +// when executing a release. +function generateReleaseId(string packageName, string version) + public + view + returns (bytes32); +``` +**Pagination** + +`getAllPackageIds` and `getAllReleaseIds` support paginated requests because it's possible that the return values for these methods could become quite large. The methods should return an `offset` that is a pointer to the next available item in a list of all items such that a caller can use it to pick up from where the previous request left off. (See [here](https://mixmax.com/blog/api-paging-built-the-right-way) for a discussion of the merits and demerits of various pagination strategies.) The `limit` parameter defines the maximum number of items a registry should return per request. + +## Rationale +The proposal hopes to accomplish the following: + ++ Define the smallest set of inputs necessary to allow registries to map package names to a set of +release versions while allowing them to use any versioning schema they choose. ++ Provide the minimum set of getter methods needed to retrieve package data from a registry so that registry aggregators can read all of their data. ++ Define a standard query that synthesizes a release identifier from a package name and version pair so that tooling can resolve specific package version requests without needing to query a registry about all of a package's releases. + +Registries may offer more complex `read` APIs that manage requests for packages within a semver range or at `latest` etc. This EIP is agnostic about how tooling or registries might implement these. It recommends that registries implement [EIP 165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) and avail themselves of resources to publish more complex interfaces such as [EIP 926](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-926.md). + +## Backwards Compatibility +No existing standard exists for package registries. The package registry currently deployed by EthPM would not comply with the standard since it implements only one of the method signatures described in the specification. + +## Implementation +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/). From 41707f3b9a34cea407862821957b4869c0c09279 Mon Sep 17 00:00:00 2001 From: Giuseppe Bertone Date: Tue, 28 Aug 2018 13:36:22 +0200 Subject: [PATCH 120/177] Fixed misleading var name (#1357) In the example addr function returns an address, not an hash --- EIPS/eip-137.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-137.md b/EIPS/eip-137.md index 0dec0340..1f9d8e87 100644 --- a/EIPS/eip-137.md +++ b/EIPS/eip-137.md @@ -61,7 +61,7 @@ var resolver = ens.resolver(node); Then, ask the resolver for the address for the contract: ``` -var hash = resolver.addr(node); +var address = resolver.addr(node); ``` Because the `namehash` procedure depends only on the name itself, this can be precomputed and inserted into a contract, removing the need for string manipulation, and permitting O(1) lookup of ENS records regardless of the number of components in the raw name. From ed41f15b9ce3c87d3d7ba212a4e84d2e8dcd8300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 28 Aug 2018 13:36:58 +0200 Subject: [PATCH 121/177] EIP-1355: Ethash 1a (#1355) * Draft Ethash 1a * Udate EIP * Speeling and grammar * EIP-1355: Add discussions-to link --- EIPS/eip-1355.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 EIPS/eip-1355.md diff --git a/EIPS/eip-1355.md b/EIPS/eip-1355.md new file mode 100644 index 00000000..cf567db5 --- /dev/null +++ b/EIPS/eip-1355.md @@ -0,0 +1,52 @@ +--- +eip: 1355 +title: Ethash 1a +author: Paweł Bylica +discussions-to: https://ethereum-magicians.org/t/eip-1355-ethash-1a/1167 +status: Draft +type: Standards Track +category: Core +created: 2018-08-26 +--- + +## Motivation + +Provide minimal set of changes to Ethash algorithm to hinder and delay the adoption of ASIC based mining. + +## Specification + +1. Define hash function `fnv1a()` as + ```python + def fnv1a(v1, v2): + return ((v1 ^ v2) * FNV1A_PRIME) % 2**32 + ``` + where `FNV1A_PRIME` is 16777499 or 16777639. +2. Change the hash function that determines the DAG item index in Ethash algorithm from `fnv() to new `fnv1a()`. + In [Main Loop](https://github.com/ethereum/wiki/wiki/Ethash#main-loop) change + ```python + p = fnv(i ^ s[0], mix[i % w]) % (n // mixhashes) * mixhashes + ``` + to + ```python + p = fnv1a(i ^ s[0], mix[i % w]) % (n // mixhashes) * mixhashes + ``` + +## Rationale + +The usual argument for decentralization and network security. + +Unless programmable, an ASIC is hardwired to perform sequential operations in a given order. fnv1a changes the order in which an exclusive-or and a multiply are applied, effectively disabling the current wave of ASICS. A second objective is minimize ethash changes to be the least disruptive, to facilitate rapid development, and to lower the analysis and test requirements. Minimizing changes to ethash reduces risk associated with updating all affected network components, and also reduces the risk of detuning existing GPUs. It's expected that this specific change would have no effect on existing GPU performance. + +Changing fnv to fnv1a has no cryptographic implications. It is merely an efficient hash function with good dispersion characteristics used to scramble DAG indexing. We remain focused on risk mitigation by reducing the need for rigorous cryptographic analysis. + + +### FNV Primes + +The 16777639 satisfies all requirements from [Wikipedia](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_prime). + +The 16777499 is preferred by FNV authors but not used in the reference FNV implementation because of historical reasons. +See [A few remarks on FNV primes](http://www.isthe.com/chongo/tech/comp/fnv/index.html#fnv-prime). + +## 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/). From dcbe69655dd4d7fcbe8a92e6d6a9c0b0b5ca231d Mon Sep 17 00:00:00 2001 From: cslarson Date: Tue, 28 Aug 2018 12:45:52 +0100 Subject: [PATCH 122/177] add difficulty bomb delay to eip 858 (#1346) * add difficulty bomb delay to eip 858 * modify specification to reflect a difficulty bomb delay of 2m blocks * Update eip-858.md --- EIPS/eip-858.md | 20 ++++++++++++-------- assets/eip-858/calculations.md | 16 +++++----------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/EIPS/eip-858.md b/EIPS/eip-858.md index 71ca2b96..47a495ca 100644 --- a/EIPS/eip-858.md +++ b/EIPS/eip-858.md @@ -1,6 +1,6 @@ --- eip: 858 -title: Reduce block reward +title: Reduce block reward and delay difficulty bomb author: Carl Larson type: Standards Track category: Core @@ -9,30 +9,34 @@ created: 2018-01-29 --- ## Simple Summary -Reduce the block reward to 1 ETH. +Reduce the block reward to 1 ETH and delay the difficulty bomb. ## Abstract The current public Ethereum network has a hashrate that corresponds to a tremendous level of energy consumption. As this energy consumption has a correlated environmental cost the network participants have an ethical obligation to ensure this cost is not higher than necessary. At this time, the most direct way to reduce this cost is to lower the block reward in order to limit the appeal of ETH mining. Unchecked growth in hashrate is also counterproductive from a security standpoint. +Recent research developments also now time the switch to POS as sometime in 2019 and as a result there is need to further delay the difficulty bomb so the network doesn't grind to a halt. + ## Motivation -The current public Ethereum network has a hashrate of 232 TH/s). This hashrate corresponds to a **lower bound** for power usage of roughly [821 MW](../assets/eip-858/calculations.md) and yearly energy consumption of 7.2 TWh (roughly 0.033% of [total](https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption) global electricity consumption). A future switch to full Proof of Stake will solve this issue entirely. Yet that switch remains enough in the future that action should be taken in the interim to limit excess harmful side affects of the present network. +The current public Ethereum network has a hashrate of 296 TH/s. This hashrate corresponds to a power usage of roughly [1 TW](../assets/eip-858/calculations.md) and yearly energy consumption of 8.8 TWh (roughly 0.04% of [total](https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption) global electricity consumption). A future switch to full Proof of Stake will solve this issue entirely. Yet that switch remains enough in the future that action should be taken in the interim to limit excess harmful side affects of the present network. ## Specification -Block reward to be changed to 1 ETH / block. -If any resulting hard forks need a name for that name to maybe be Perinthos. + +Delay difficulty bomb by 2,000,000 blocks +Adjust block, uncle, and nephew rewards to reflect a new block reward of 1 ETH. ## Rationale -partly TBD +This will delay the difficulty bomb by roughly a year. The difficulty bomb remains a community supported mechanism to aid a future transition to POS. + The network hashrate provides security by reducing the likelihood that an adversary could mount a 51% attack. A static block reward means that factors (price) may be such that participation in mining grows unchecked. This growth may be counterproductive and work to also grow and potential pool of adversaries. The means we have to arrest this growth is to reduce the appeal of mining and the most direct way to do that is to reduce the block reward. ## Backwards Compatibility -This EIP is consensus incompatible with the current public Ethereum chain and would cause a hard fork when enacted. The resulting fork would allow users to chose between two chains: a chain with a block reward of 1 ETH/block and another with a block reward of 3 ETH/block. This is a good choice to allow users to make. +This EIP is consensus incompatible with the current public Ethereum chain and would cause a hard fork when enacted. The resulting fork would allow users to chose between two chains: a chain with a block reward of 1 ETH/block and another with a block reward of 3 ETH/block. This is a good choice to allow users to make. In addition, the difficulty bomb would be delayed - ensuring the network would not grind to a halt. ## Test Cases Tests have, as yet, not been completed. ## Implementation -A [fork of the go repo](https://github.com/cslarson/go-ethereum/tree/reduce-block-reward) with changes to reflect a block reward reduced to 1 ETH, activated at a fork to be called Perinthos. +No implementation including both block reward and difficulty adjustment is currently available. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/assets/eip-858/calculations.md b/assets/eip-858/calculations.md index 9494cb7b..b7b1ba70 100644 --- a/assets/eip-858/calculations.md +++ b/assets/eip-858/calculations.md @@ -1,6 +1,6 @@ | Variable | Symbol | Value | Unit | Source | | -------------------|--------------|---------------|---------------|--------| -| Network Hashrate |HN | 232001 | GH/s | https://etherscan.io/chart/hashrate | +| Network Hashrate |HN | 296000 | GH/s | https://etherscan.io/chart/hashrate | | GPU Hashrate |HM | 31.2 | MH/s | http://www.legitreviews.com/geforce-gtx-1070-ethereum-mining-small-tweaks-great-hashrate-low-power_195451 | | GPU Power |PM | 110.6 | W | https://www.reddit.com/r/ethereum/comments/7vewys/10000_tons_co2_per_day_and_climbing_eip_858/dtrswyz/ | @@ -11,20 +11,14 @@ A baseline value for network power consumption can be found by multiplying the t > PN = HN x PM / HM > -> PN = 232001 (GH/s) x 110.6 (W) x 1000 (MH/GH) / ( 31.2 (MH/s) x 10^6 (W/MW) ) +> PN = 296000 (GH/s) x 110.6 (W) x 1000 (MH/GH) / ( 31.2 (MH/s) x 10^6 (W/MW) ) > -> PN = 821 MW +> PN = 1049 MW -As a side note, people often confuse power (W) and energy (power x time, eg. Wh). For instance, assuming an average daily PNd of 821 MW we can calculate that days Energy consumption by multiplying by the number of hours in a day. +As a side note, people often confuse power (W) and energy (power x time, eg. Wh). For instance, assuming an average daily PNd of 1049 MW we can calculate that days Energy consumption by multiplying by the number of hours in a day. > ENd = PNd x Td > -> ENd = 821 (MW) x 24 (h/d) / 1000 (GW/MW) +> ENd = 1049 (MW) x 24 (h/d) / 1000 (GW/MW) > > ENd = 19.7 GWh - - - -## Network CO2 contribution - -Work in progress From 6e9af9b8f5fe3a8cffc412b86009408903313290 Mon Sep 17 00:00:00 2001 From: Afri Schoedon <5chdn@users.noreply.github.com> Date: Tue, 28 Aug 2018 13:46:52 +0200 Subject: [PATCH 123/177] 1295: fix author field in header (#1349) --- EIPS/eip-1295.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1295.md b/EIPS/eip-1295.md index 8a6345a2..5952b0fd 100644 --- a/EIPS/eip-1295.md +++ b/EIPS/eip-1295.md @@ -1,7 +1,7 @@ --- eip: 1295 title: Modify Ethereum PoW Incentive Structure and Diffuse Difficulty Bomb -author: +author: Brian Venturo (@atlanticcrypto) discussions-to: https://github.com/atlanticcrypto/Discussion/issues/1 status: Draft type: Standards Track From 79b2957a48304d50810833c450efbadf9b95a138 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Tue, 28 Aug 2018 07:48:49 -0400 Subject: [PATCH 124/177] feat: Add Twitter Card metadata (#1345) This adds Twitter Card metadata to the site, using the format that [`jekyll-seo-tag`](https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/usage.md), a dependency of Minima, understands. This should make the site render better on Twitter. I tested this locally and it seemed to work. --- _config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_config.yml b/_config.yml index b5b2020d..1388c5e2 100644 --- a/_config.yml +++ b/_config.yml @@ -29,6 +29,9 @@ header_pages: - erc.html - informational.html - meta.html +twitter: + card: summary + username: ethereum # Build settings markdown: kramdown From 374a3d81543335867ad682960626d4347c6532f8 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 28 Aug 2018 12:50:27 +0100 Subject: [PATCH 125/177] ERC-1328 - WalletConnect Standard URI Format (#1330) * eip-1328 * draft * review changes --- EIPS/eip-1328.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 EIPS/eip-1328.md diff --git a/EIPS/eip-1328.md b/EIPS/eip-1328.md new file mode 100644 index 00000000..90fb6d21 --- /dev/null +++ b/EIPS/eip-1328.md @@ -0,0 +1,49 @@ +--- +eip: 1328 +title: WalletConnect Standard URI Format +authors: ligi , Pedro Gomes +type: Standards Track +category: ERC +status: Draft +created: 2018-08-15 +discussions-to: https://ethereum-magicians.org/t/wallet-connect-eip/850 +requires: 831 +--- + +## Simple Summary + +A standard to create WalletConnect URIs for establishing connections between wallets and applications. + +## 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. + +## Specification + +### Syntax + +Function call URIs follow the ERC-831 URI format, with the following parameters: + + request = "ethereum" ":" [ "wc-" ]sessionID [ "@" version ][ "?" parameters ] + sessionID = STRING + version = 1*DIGIT + parameters = parameter *( "&" parameter ) + parameter = key "=" value + key = "name" / "bridge" / "symKey" + value = NUMBER / STRING + +### Semantics + +Required parameters are dependent on the WalletConnect standard version which currently is specified to only include mobile-to-desktop connection sessions which only require `name` which describes the dapp name, `bridge` which includes the bridge URL, `symKey` which includes the symmetric key in base64. + +## Rationale + +The need for this ERC stems from the discussion to move away from JSON format used in current beta version of the WalletConnect standard 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 for other compatible EIPs using the ERC-831 URI format for Ethereum. Also by using a URI instead of a JSON inside the QR-Code the Android Intent system can be leveraged. + +## References + +1. ERC-831, http://eips.ethereum.org/EIPS/eip-831 + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 0e0bf24830a032b66462749d0384702dc56ee283 Mon Sep 17 00:00:00 2001 From: DB Hurley Date: Tue, 28 Aug 2018 08:57:47 -0400 Subject: [PATCH 126/177] Automatically merged updates to draft EIP(s) 725 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-725.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-725.md b/EIPS/eip-725.md index 4744f8fd..c6aa3d28 100644 --- a/EIPS/eip-725.md +++ b/EIPS/eip-725.md @@ -241,13 +241,13 @@ MUST be triggered when `approve` was called and the claim was successfully added ## Rationale -This specification was chosen to allow most flexibility and experimention around identity. By having each identity in a separate contract it allows for cross idenity compatibility, but at the same time extra and altered functionality for new use cases. +This specification was chosen to allow most flexibility and experimention around identity. By having each identity in a separate contract it allows for cross identity compatibility, but at the same time extra and altered functionality for new use cases. The main critic of this standard is the verification where each identity that issues a claim, also should have a separate CLAIM signing key attached. While [#ERC780](https://github.com/ethereum/EIPs/issues/780) uses a standardised registry to assign claims to addresses. Both systems could work in conjunction and should be explored. While also off-chain claims using DID verifiable claims and merkle tries can be added as claims and should be explored. -The rationale of this standard is to function as an open and veryf flexible container for idenity. +The rationale of this standard is to function as an open and very flexible container for identity. ## Implementation @@ -299,4 +299,4 @@ contract ERC725 { - [Sovrin Foundation Self Sovereign Identity](https://sovrin.org/wp-content/uploads/2017/06/The-Inevitable-Rise-of-Self-Sovereign-Identity.pdf) ## 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](https://creativecommons.org/publicdomain/zero/1.0/). From 1003d75c823f2e67bde774d192a63074937cb4d6 Mon Sep 17 00:00:00 2001 From: Logan Saether Date: Tue, 28 Aug 2018 15:49:11 +0100 Subject: [PATCH 127/177] Automatically merged updates to draft EIP(s) 725 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-725.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-725.md b/EIPS/eip-725.md index c6aa3d28..94106089 100644 --- a/EIPS/eip-725.md +++ b/EIPS/eip-725.md @@ -281,9 +281,10 @@ contract ERC725 { } function getKey(bytes32 _key) public constant returns(uint256[] purposes, uint256 keyType, bytes32 key); - function keyHasPurpose(bytes32 _key, uint256 purpose) constant returns(bool exists); - function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys); + function keyHasPurpose(bytes32 _key, uint256 _purpose) public constant returns (bool exists); + function getKeysByPurpose(uint256 _purpose) public constant returns (bytes32[] keys); function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success); + function removeKey(bytes32 _key, uint256 _purpose) public returns (bool success); function execute(address _to, uint256 _value, bytes _data) public returns (uint256 executionId); function approve(uint256 _id, bool _approve) public returns (bool success); } From 46961da17b5583fc90fbe9de76e0c7aef681b2b5 Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Tue, 28 Aug 2018 18:55:19 +0200 Subject: [PATCH 128/177] Automatically merged updates to draft EIP(s) 820 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-820.md | 138 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 38 deletions(-) diff --git a/EIPS/eip-820.md b/EIPS/eip-820.md index 3dda93b8..6b219b54 100644 --- a/EIPS/eip-820.md +++ b/EIPS/eip-820.md @@ -23,9 +23,9 @@ The rest of the world can query this registry to ask if a specific address imple This registry MAY be deployed on any chain and will share the exact same address. -Interfaces with zeroes (`0`) as the the last 28 bytes are considered [ERC165] interfaces, and this registry SHALL forward the call to the contract to see if it implements the interface. +Interfaces with zeroes (`0`) as the last 28 bytes are considered [ERC165] interfaces, and this registry SHALL forward the call to the contract to see if it implements the interface. -This contract also acts as an [ERC165] cache in order to reduce gas consumption. +This contract also acts as an [ERC165] cache to reduce gas consumption. ## Motivation @@ -42,20 +42,47 @@ This standard also provides a unique address for all chains. Thus solving the pr > This is an exact copy of the code of the [ERC820 registry smart contract]. ``` solidity +/* ERC820: Pseudo-introspection Registry Contract + * by Jordi Baylina and Jacques Dafflon + * + * To the extent possible under law, Jordi Baylina and Jacques Dafflon who + * associated CC0 with the ERC820: Pseudo-introspection Registry Contract have + * waived all copyright and related or neighboring rights to the + * ERC820: Pseudo-introspection Registry Contract. + * + * You should have received a copy of the CC0 legalcode along with this work. + * If not, see . + * + * ███████╗██████╗ ██████╗ █████╗ ██████╗ ██████╗ + * ██╔════╝██╔══██╗██╔════╝██╔══██╗╚════██╗██╔═████╗ + * █████╗ ██████╔╝██║ ╚█████╔╝ █████╔╝██║██╔██║ + * ██╔══╝ ██╔══██╗██║ ██╔══██╗██╔═══╝ ████╔╝██║ + * ███████╗██║ ██║╚██████╗╚█████╔╝███████╗╚██████╔╝ + * ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝ + * + * ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗ ██╗ + * ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝ + * ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ ██████╔╝ ╚████╔╝ + * ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══██╗ ╚██╔╝ + * ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ██║ ██║ ██║ + * ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ + * + */ pragma solidity 0.4.24; -// IV is a simply value to have a "nice" address for the registry, starting with `0x820`. -// IV: 12302 +// IV is value needed to have a vanity address starting with `0x820`. +// IV: 1200 /// @dev The interface a contract MUST implement if it is the implementer of -/// some interface for any address other than itself. +/// some (other) interface for any address other than itself. interface ERC820ImplementerInterface { /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not. /// @param addr Address for which the contract will implement the interface /// @param interfaceHash keccak256 hash of the name of the interface - /// @return ERC820_ACCEPT_MAGIC only if the contract implements `ìnterfaceHash` for the address `addr`. + /// @return ERC820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`. function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32); } + /// @title ERC820 Pseudo-introspection Registry Contract /// @author Jordi Baylina and Jacques Dafflon /// @notice This contract is the official implementation of the ERC820 Registry. @@ -65,7 +92,7 @@ contract ERC820Registry { bytes4 constant INVALID_ID = 0xffffffff; /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`). bytes4 constant ERC165ID = 0x01ffc9a7; - /// @notice Magic value which is returned if a contrct implements an interface on behalf of some other address. + /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address. bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC820_ACCEPT_MAGIC")); mapping (address => mapping(bytes32 => address)) interfaces; @@ -162,7 +189,7 @@ contract ERC820Registry { return interfaces[_contract][_interfaceId] != 0; } - /// @notice Updates the cache with whether contract implements an ERC165 interface or not. + /// @notice Updates the cache with whether the contract implements an ERC165 interface or not. /// @param _contract Address of the contract for which to update the cache. /// @param _interfaceId ERC165 interface for which to update the cache. function updateERC165Cache(address _contract, bytes4 _interfaceId) public { @@ -233,42 +260,44 @@ contract ERC820Registry { Below is the raw transaction which MUST be used to deploy the smart contract on any chain. ``` -0xf90ab18085174876e800830c35008080b90a5e608060405234801561001057600080fd5b50610a3e806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf5780635df8122f146100fc57806365ba36c114610123578063a41e7d511461018e578063aabbb8ca146101bc578063b7056765146101e0578063f712f3e814610222575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610250565b005b3480156100cb57600080fd5b506100e0600160a060020a036004351661054b565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a0360043581169060243516610597565b34801561012f57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261017c9436949293602493928401919081908401838280828437509497506106aa9650505050505050565b60408051918252519081900360200190f35b34801561019a57600080fd5b506100bd600160a060020a0360043516600160e060020a031960243516610774565b3480156101c857600080fd5b506100e0600160a060020a03600435166024356107fe565b3480156101ec57600080fd5b5061020e600160a060020a0360043516600160e060020a031960243516610878565b604080519115158252519081900360200190f35b34801561022e57600080fd5b5061020e600160a060020a0360043516600160e060020a03196024351661092d565b6000600160a060020a038416156102675783610269565b335b9050336102758261054b565b600160a060020a0316146102d3576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6102dc836109a3565b15610331576040805160e560020a62461bcd02815260206004820152601960248201527f4d757374206e6f74206265206120455243313635206861736800000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103525750600160a060020a0382163314155b156104da5760405160200180807f4552433832305f4143434550545f4d414749430000000000000000000000000081525060130190506040516020818303038152906040526040518082805190602001908083835b602083106103c65780518252601f1990920191602091820191016103a7565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207ff0083250000000000000000000000000000000000000000000000000000000008352600160a060020a038881166004850152602484018b90529451909650938816945063f0083250936044808401945091929091908290030181600087803b15801561045957600080fd5b505af115801561046d573d6000803e3d6000fd5b505050506040513d602081101561048357600080fd5b5051146104da576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a038082166000908152600160205260408120549091161515610575575080610592565b50600160a060020a03808216600090815260016020526040902054165b919050565b6000600160a060020a038316156105ae57826105b0565b335b9050336105bc8261054b565b600160a060020a03161461061a576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b80600160a060020a031682600160a060020a031614610639578161063c565b60005b600160a060020a03828116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519185169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a3505050565b6000816040516020018082805190602001908083835b602083106106df5780518252601f1990920191602091820191016106c0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106107425780518252601f199092019160209182019101610723565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b61077e8282610878565b61078957600061078b565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b60008080600160a060020a038516156108175784610819565b335b9150610824846109a3565b15610849575082610835828261092d565b610840576000610842565b815b9250610870565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b600080806108a6857f01ffc9a7000000000000000000000000000000000000000000000000000000006109c5565b90925090508115806108b6575080155b156108c45760009250610870565b6108d685600160e060020a03196109c5565b90925090508115806108e757508015155b156108f55760009250610870565b6108ff85856109c5565b90925090506001821480156109145750806001145b156109225760019250610870565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff16151561096e5761096e8383610774565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a723058209f4f0ded519f65ffc31c58c2e588b1713ee5a922e97928e2ccbd91e39128037700291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +0xf90ab18085174876e800830c35008080b90a5e608060405234801561001057600080fd5b50610a3e806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf5780635df8122f146100fc57806365ba36c114610123578063a41e7d511461018e578063aabbb8ca146101bc578063b7056765146101e0578063f712f3e814610222575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610250565b005b3480156100cb57600080fd5b506100e0600160a060020a036004351661054b565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a0360043581169060243516610597565b34801561012f57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261017c9436949293602493928401919081908401838280828437509497506106aa9650505050505050565b60408051918252519081900360200190f35b34801561019a57600080fd5b506100bd600160a060020a0360043516600160e060020a031960243516610774565b3480156101c857600080fd5b506100e0600160a060020a03600435166024356107fe565b3480156101ec57600080fd5b5061020e600160a060020a0360043516600160e060020a031960243516610878565b604080519115158252519081900360200190f35b34801561022e57600080fd5b5061020e600160a060020a0360043516600160e060020a03196024351661092d565b6000600160a060020a038416156102675783610269565b335b9050336102758261054b565b600160a060020a0316146102d3576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6102dc836109a3565b15610331576040805160e560020a62461bcd02815260206004820152601960248201527f4d757374206e6f74206265206120455243313635206861736800000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103525750600160a060020a0382163314155b156104da5760405160200180807f4552433832305f4143434550545f4d414749430000000000000000000000000081525060130190506040516020818303038152906040526040518082805190602001908083835b602083106103c65780518252601f1990920191602091820191016103a7565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207ff0083250000000000000000000000000000000000000000000000000000000008352600160a060020a038881166004850152602484018b90529451909650938816945063f0083250936044808401945091929091908290030181600087803b15801561045957600080fd5b505af115801561046d573d6000803e3d6000fd5b505050506040513d602081101561048357600080fd5b5051146104da576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a038082166000908152600160205260408120549091161515610575575080610592565b50600160a060020a03808216600090815260016020526040902054165b919050565b6000600160a060020a038316156105ae57826105b0565b335b9050336105bc8261054b565b600160a060020a03161461061a576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b80600160a060020a031682600160a060020a031614610639578161063c565b60005b600160a060020a03828116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519185169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a3505050565b6000816040516020018082805190602001908083835b602083106106df5780518252601f1990920191602091820191016106c0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106107425780518252601f199092019160209182019101610723565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b61077e8282610878565b61078957600061078b565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b60008080600160a060020a038516156108175784610819565b335b9150610824846109a3565b15610849575082610835828261092d565b610840576000610842565b815b9250610870565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b600080806108a6857f01ffc9a7000000000000000000000000000000000000000000000000000000006109c5565b90925090508115806108b6575080155b156108c45760009250610870565b6108d685600160e060020a03196109c5565b90925090508115806108e757508015155b156108f55760009250610870565b6108ff85856109c5565b90925090506001821480156109145750806001145b156109225760019250610870565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff16151561096e5761096e8383610774565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a72305820aebcc5581490dc97cade67a16777bfda36414f87fac1422cc9f37c2d60691cea00291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00820820820820820820820820820820820820820820820820820820820820820 ``` -The string of `a`'s at the end of the transaction is the `s` of the signature. From this pattern, one can clearly deduce that it is a deterministic signature generated by a human. +The string of `820`'s at the end of the transaction is the `s` of the signature. From this pattern, one can clearly deduce that it is a deterministic signature generated by a human. ### Deployment Method -This contract is going to be deployed using [Nick]'s Method. This method works as follows: +This contract is going to be deployed using the keyless deployment method—also known as [Nick]'s method—which relies on a single-use address. (See [Nick's article] for more details). This method works as follows: 1. Generate a transaction which deploys the contract from a new random account. - - This transaction MUST NOT use [ERC155] in order to work on any chain. - - This transaction MUST have a relatively high gas price in order to be deployed on any chain. In this case, it is going to be 100 Gwei. + - This transaction MUST NOT use [EIP155] in order to work on any chain. + - This transaction MUST have a relatively high gas price to be deployed on any chain. In this case, it is going to be 100 Gwei. 2. Set the `v`, `r`, `s` of the transaction signature to the following values: ``` v: 27 r: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 - s: 0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + s: 0x0820820820820820820820820820820820820820820820820820820820820820 ``` -This nice `s` value is a "random number" generated deterministically by a human. +This nice `s` value---made of a repetion of `820`---is a predicatable "random number" generated deterministically by a human. -3. We recover the sender of this transaction. Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account. +3. We recover the sender of this transaction, i.e. the deployment account. + + > Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account. 4. Send Ether to this deployment account. 5. Broadcast the transaction. -This operation can be done in any chain, guaranteeing that the contract address is going to always be the same and nobody will be able to mess up that address with a different contract. +This operation can be done on any chain, guaranteeing that the contract address is going to always be the same and nobody will be able to mess up that address with a different contract. ### Special Registry Deployment Account ``` -0x6c3FB9d29823aB78F93d46C8bF40Abf7a70ED9be +0xC3AdeE9B2E23837DF6259A984Af7a437dE4E2ab6 ``` This account is generated by reverse engineering it from its signature for the transaction. This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction. @@ -276,13 +305,14 @@ This account is generated by reverse engineering it from its signature for the t ### Deployed contract ``` -0x820A6e5561A66f60c6546a74001db369bbFf238D +0x820d0Bc4d0AD9E0E7dc19BD8cF9C566FC86054ce ``` The contract has the address above for every chain it is deployed to.
    Raw metadata of ./contracts/ERC820Registry.sol -
    {
    +
    +{
       "compiler": {
         "version": "0.4.24+commit.e67f0147"
       },
    @@ -576,7 +606,7 @@ The contract has the address above for every chain it is deployed to.
               "notice": "Sets the `_newManager` as manager for the `_addr` address. The new manager will be able to call `setInterfaceImplementer` for `_addr`."
             },
             "updateERC165Cache(address,bytes4)": {
    -          "notice": "Updates the cache with whether contract implements an ERC165 interface or not."
    +          "notice": "Updates the cache with whether the contract implements an ERC165 interface or not."
             }
           }
         }
    @@ -595,12 +625,13 @@ The contract has the address above for every chain it is deployed to.
       },
       "sources": {
         "./contracts/ERC820Registry.sol": {
    -      "content": "pragma solidity 0.4.24;\n// IV is a simply value to have a \"nice\" address for the registry, starting with `0x820`.\n// IV: 12302\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some interface for any address other than itself.\ninterface ERC820ImplementerInterface {\n    /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.\n    /// @param addr Address for which the contract will implement the interface\n    /// @param interfaceHash keccak256 hash of the name of the interface\n    /// @return ERC820_ACCEPT_MAGIC only if the contract implements `ìnterfaceHash` for the address `addr`.\n    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32);\n}\n\n/// @title ERC820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820\ncontract ERC820Registry {\n    /// @notice ERC165 Invalid ID.\n    bytes4 constant INVALID_ID = 0xffffffff;\n    /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).\n    bytes4 constant ERC165ID = 0x01ffc9a7;\n    /// @notice Magic value which is returned if a contrct implements an interface on behalf of some other address.\n    bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked(\"ERC820_ACCEPT_MAGIC\"));\n\n    mapping (address => mapping(bytes32 => address)) interfaces;\n    mapping (address => address) managers;\n    mapping (address => mapping(bytes4 => bool)) erc165Cached;\n\n    /// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.\n    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n    /// @notice Indicates `newManager` is the address of the new manager for `addr`.\n    event ManagerChanged(address indexed addr, address indexed newManager);\n\n    /// @notice Query if an address implements an interface and through which contract.\n    /// @param _addr Address being queried for the implementer of an interface.\n    /// (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// E.g., `web3.utils.keccak256('ERC777Token')`.\n    /// @return The address of the contract which implements the interface `_interfaceHash` for `_addr`\n    /// or `0x0` if `_addr` did not register an implementer for this interface.\n    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        if (isERC165Interface(_interfaceHash)) {\n            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0;\n        }\n        return interfaces[addr][_interfaceHash];\n    }\n\n    /// @notice Sets the contract which implements a specific interface for an address.\n    /// Only the manager defined for that address can set it.\n    /// (Each address is the manager for itself until it sets a new manager.)\n    /// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface.\n    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n\n        require(!isERC165Interface(_interfaceHash), \"Must not be a ERC165 hash\");\n        if (_implementer != 0 && _implementer != msg.sender) {\n            require(\n                ERC820ImplementerInterface(_implementer)\n                    .canImplementInterfaceForAddress(addr, _interfaceHash) == ERC820_ACCEPT_MAGIC,\n                \"Does not implement the interface\"\n            );\n        }\n        interfaces[addr][_interfaceHash] = _implementer;\n        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);\n    }\n\n    /// @notice Sets the `_newManager` as manager for the `_addr` address.\n    /// The new manager will be able to call `setInterfaceImplementer` for `_addr`.\n    /// @param _addr Address for which to set the new manager. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _newManager Address of the new manager for `addr`. (Pass `0x0` to reset the manager to `_addr` itself.)\n    function setManager(address _addr, address _newManager) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n        managers[addr] = _newManager == addr ? 0 : _newManager;\n        emit ManagerChanged(addr, _newManager);\n    }\n\n    /// @notice Get the manager of an address.\n    /// @param _addr Address for which to return the manager.\n    /// @return Address of the manager for a given address.\n    function getManager(address _addr) public view returns(address) {\n        // By default the manager of an address is the same address\n        if (managers[_addr] == 0) {\n            return _addr;\n        } else {\n            return managers[_addr];\n        }\n    }\n\n    /// @notice Compute the keccak256 hash of an interface given its name.\n    /// @param _interfaceName Name of the interface.\n    /// @return The keccak256 hash of an interface name.\n    function interfaceHash(string _interfaceName) public pure returns(bytes32) {\n        return keccak256(abi.encodePacked(_interfaceName));\n    }\n\n    /* --- ERC165 Related Functions --- */\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not.\n    /// The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    /// @dev This function may modify the state when updating the cache. However, this function must have the `view`\n    /// modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the ERC165 cache\n    /// is updated.\n    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        if (!erc165Cached[_contract][_interfaceId]) {\n            updateERC165Cache(_contract, _interfaceId);\n        }\n        return interfaces[_contract][_interfaceId] != 0;\n    }\n\n    /// @notice Updates the cache with whether contract implements an ERC165 interface or not.\n    /// @param _contract Address of the contract for which to update the cache.\n    /// @param _interfaceId ERC165 interface for which to update the cache.\n    function updateERC165Cache(address _contract, bytes4 _interfaceId) public {\n        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0;\n        erc165Cached[_contract][_interfaceId] = true;\n    }\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        uint256 success;\n        uint256 result;\n\n        (success, result) = noThrowCall(_contract, ERC165ID);\n        if (success == 0 || result == 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, INVALID_ID);\n        if (success == 0 || result != 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, _interfaceId);\n        if (success == 1 && result == 1) {\n            return true;\n        }\n        return false;\n    }\n\n    /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n    /// @param _interfaceHash The hash to check.\n    /// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise.\n    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n    }\n\n    function noThrowCall(address _contract, bytes4 _interfaceId)\n        internal view returns (uint256 success, uint256 result)\n    {\n        bytes4 erc165ID = ERC165ID;\n\n        assembly {\n                let x := mload(0x40)               // Find empty storage location using \"free memory pointer\"\n                mstore(x, erc165ID)                // Place signature at begining of empty storage\n                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n                success := staticcall(\n                    30000,                         // 30k gas\n                    _contract,                     // To addr\n                    x,                             // Inputs are stored at location x\n                    0x08,                          // Inputs are 8 bytes long\n                    x,                             // Store output over input (saves space)\n                    0x20                           // Outputs are 32 bytes long\n                )\n\n                result := mload(x)                 // Load the result\n        }\n    }\n}\n",
    -      "keccak256": "0xb7b2e57b5db209f6d6c10c9172abe541be3010bfb194e8fadb460fd58608a9ee"
    +      "content": "/* ERC820: Pseudo-introspection Registry Contract\n * by Jordi Baylina and Jacques Dafflon\n *\n * To the extent possible under law, Jordi Baylina and Jacques Dafflon who\n * associated CC0 with the ERC820: Pseudo-introspection Registry Contract have\n * waived all copyright and related or neighboring rights to the\n * ERC820: Pseudo-introspection Registry Contract.\n *\n * You should have received a copy of the CC0 legalcode along with this work.\n * If not, see .\n *\n *    ███████╗██████╗  ██████╗ █████╗ ██████╗  ██████╗\n *    ██╔════╝██╔══██╗██╔════╝██╔══██╗╚════██╗██╔═████╗\n *    █████╗  ██████╔╝██║     ╚█████╔╝ █████╔╝██║██╔██║\n *    ██╔══╝  ██╔══██╗██║     ██╔══██╗██╔═══╝ ████╔╝██║\n *    ███████╗██║  ██║╚██████╗╚█████╔╝███████╗╚██████╔╝\n *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝\n *\n *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗\n *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝\n *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝\n *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝\n *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║\n *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝\n *\n */\npragma solidity 0.4.24;\n// IV is value needed to have a vanity address starting with `0x820`.\n// IV: 1200\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some (other) interface for any address other than itself.\ninterface ERC820ImplementerInterface {\n    /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.\n    /// @param addr Address for which the contract will implement the interface\n    /// @param interfaceHash keccak256 hash of the name of the interface\n    /// @return ERC820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`.\n    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32);\n}\n\n\n/// @title ERC820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820\ncontract ERC820Registry {\n    /// @notice ERC165 Invalid ID.\n    bytes4 constant INVALID_ID = 0xffffffff;\n    /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).\n    bytes4 constant ERC165ID = 0x01ffc9a7;\n    /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.\n    bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked(\"ERC820_ACCEPT_MAGIC\"));\n\n    mapping (address => mapping(bytes32 => address)) interfaces;\n    mapping (address => address) managers;\n    mapping (address => mapping(bytes4 => bool)) erc165Cached;\n\n    /// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.\n    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n    /// @notice Indicates `newManager` is the address of the new manager for `addr`.\n    event ManagerChanged(address indexed addr, address indexed newManager);\n\n    /// @notice Query if an address implements an interface and through which contract.\n    /// @param _addr Address being queried for the implementer of an interface.\n    /// (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// E.g., `web3.utils.keccak256('ERC777Token')`.\n    /// @return The address of the contract which implements the interface `_interfaceHash` for `_addr`\n    /// or `0x0` if `_addr` did not register an implementer for this interface.\n    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        if (isERC165Interface(_interfaceHash)) {\n            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0;\n        }\n        return interfaces[addr][_interfaceHash];\n    }\n\n    /// @notice Sets the contract which implements a specific interface for an address.\n    /// Only the manager defined for that address can set it.\n    /// (Each address is the manager for itself until it sets a new manager.)\n    /// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface.\n    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n\n        require(!isERC165Interface(_interfaceHash), \"Must not be a ERC165 hash\");\n        if (_implementer != 0 && _implementer != msg.sender) {\n            require(\n                ERC820ImplementerInterface(_implementer)\n                    .canImplementInterfaceForAddress(addr, _interfaceHash) == ERC820_ACCEPT_MAGIC,\n                \"Does not implement the interface\"\n            );\n        }\n        interfaces[addr][_interfaceHash] = _implementer;\n        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);\n    }\n\n    /// @notice Sets the `_newManager` as manager for the `_addr` address.\n    /// The new manager will be able to call `setInterfaceImplementer` for `_addr`.\n    /// @param _addr Address for which to set the new manager. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _newManager Address of the new manager for `addr`. (Pass `0x0` to reset the manager to `_addr` itself.)\n    function setManager(address _addr, address _newManager) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n        managers[addr] = _newManager == addr ? 0 : _newManager;\n        emit ManagerChanged(addr, _newManager);\n    }\n\n    /// @notice Get the manager of an address.\n    /// @param _addr Address for which to return the manager.\n    /// @return Address of the manager for a given address.\n    function getManager(address _addr) public view returns(address) {\n        // By default the manager of an address is the same address\n        if (managers[_addr] == 0) {\n            return _addr;\n        } else {\n            return managers[_addr];\n        }\n    }\n\n    /// @notice Compute the keccak256 hash of an interface given its name.\n    /// @param _interfaceName Name of the interface.\n    /// @return The keccak256 hash of an interface name.\n    function interfaceHash(string _interfaceName) public pure returns(bytes32) {\n        return keccak256(abi.encodePacked(_interfaceName));\n    }\n\n    /* --- ERC165 Related Functions --- */\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not.\n    /// The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    /// @dev This function may modify the state when updating the cache. However, this function must have the `view`\n    /// modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the ERC165 cache\n    /// is updated.\n    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        if (!erc165Cached[_contract][_interfaceId]) {\n            updateERC165Cache(_contract, _interfaceId);\n        }\n        return interfaces[_contract][_interfaceId] != 0;\n    }\n\n    /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n    /// @param _contract Address of the contract for which to update the cache.\n    /// @param _interfaceId ERC165 interface for which to update the cache.\n    function updateERC165Cache(address _contract, bytes4 _interfaceId) public {\n        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0;\n        erc165Cached[_contract][_interfaceId] = true;\n    }\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        uint256 success;\n        uint256 result;\n\n        (success, result) = noThrowCall(_contract, ERC165ID);\n        if (success == 0 || result == 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, INVALID_ID);\n        if (success == 0 || result != 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, _interfaceId);\n        if (success == 1 && result == 1) {\n            return true;\n        }\n        return false;\n    }\n\n    /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n    /// @param _interfaceHash The hash to check.\n    /// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise.\n    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n    }\n\n    function noThrowCall(address _contract, bytes4 _interfaceId)\n        internal view returns (uint256 success, uint256 result)\n    {\n        bytes4 erc165ID = ERC165ID;\n\n        assembly {\n                let x := mload(0x40)               // Find empty storage location using \"free memory pointer\"\n                mstore(x, erc165ID)                // Place signature at begining of empty storage\n                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n                success := staticcall(\n                    30000,                         // 30k gas\n                    _contract,                     // To addr\n                    x,                             // Inputs are stored at location x\n                    0x08,                          // Inputs are 8 bytes long\n                    x,                             // Store output over input (saves space)\n                    0x20                           // Outputs are 32 bytes long\n                )\n\n                result := mload(x)                 // Load the result\n        }\n    }\n}\n",
    +      "keccak256": "0xbc18b6a4ad0aeb405e2ebae976b0ab653c17b3ba60eeaf0f1cf740a26bdcb7f1"
         }
       },
       "version": 1
    -}
    +}
    +
    ### Interface name @@ -624,6 +655,20 @@ Examples: Any interface where the last 28 bytes are zeroes (`0`) SHALL be considered an [ERC165] interface. +**[ERC165] Cache** + +Whether a contract implements an [ERC165] interface or not is automatically cached during [lookup] if the lookup is part of a transaction. + +If a contract dynamically changes its interface, that contract SHOULD update the cache manually---there is no automatic cache invalidation or cache update. The cache update MUST be done using the `updateERC165Cache` function: + +``` solidity +function updateERC165Cache(address _contract, bytes4 _interfaceId) public +``` + +> **parameters** +> `_contract`: Address of the contract for which to update the cache. +> `_interfaceHash`: ERC165 interface for which to update the cache. + #### **Private User-defined Interfaces** This scheme is extensible. You MAY make up your own interface name and raise awareness to get other people to implement it and then check for those implementations. Have fun but please, you MUST not conflict with the reserved designations above. @@ -638,7 +683,7 @@ function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address Sets the contract that will handle a specific interface. -Only a `manager` defined for that address can set it. (Each address is the manager for itself until a new manager is defined) +Only the `manager` defined for that address can set it. (Each address is the manager for itself, see the [manager] section for more details.) *NOTE*: If `_addr` and `_implementer` are two different addresses, then: @@ -648,8 +693,8 @@ Only a `manager` defined for that address can set it. (Each address is the manag *NOTE*: The `_interfaceHash` MUST NOT be an [ERC165] interface—it MUST NOT end with 28 zeroes (`0`). > **parameters** -> `_addr` Address to define the interface for (if `_addr == 0` them `msg.sender` is assumed) -> `_interfaceHash` `keccak256` hash of the name of the interface as a string, for example `web3.utils.keccak256('ERC777TokensRecipient')` for the ERC777TokensRecipient interface. +> `_addr`: Address to define the interface for (if `_addr == 0` them `msg.sender`: is assumed) +> `_interfaceHash`: `keccak256` hash of the name of the interface as a string, for example `web3.utils.keccak256('ERC777TokensRecipient')` for the ERC777TokensRecipient interface. ### Get An Implementation Of An Interface For An Address @@ -661,12 +706,12 @@ function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) public v Query if an address implements an interface and through which contract. -*NOTE*: If the last 28 bytes of the `_interfaceHash` are zeroes (`0`), then the first 4 bytes are considered an [ERC165] interface and the registry SHALL forward the call to the contract at `_addr` to see if it implements the [ERC165] interface (the first 4 bytes of `_interfaceHash`). The registry SHALL also cache [ERC165] queries in order to reduce gas consumption. Anyone MAY call the `erc165UpdateCache` function to update whether a contract implements an interface or not. +*NOTE*: If the last 28 bytes of the `_interfaceHash` are zeroes (`0`), then the first 4 bytes are considered an [ERC165] interface and the registry SHALL forward the call to the contract at `_addr` to see if it implements the [ERC165] interface (the first 4 bytes of `_interfaceHash`). The registry SHALL also cache [ERC165] queries to reduce gas consumption. Anyone MAY call the `erc165UpdateCache` function to update whether a contract implements an interface or not. > **parameters** -> `_addr` Address being queried for the implementer of an interface. (If `_addr == 0` them `msg.sender` is assumed.) -> `_interfaceHash` keccak256 hash of the name of the interface as a string. E.g. `web3.utils.keccak256('ERC777Token')` -> **returns:** The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0x0` if `_addr` did not registeran implemeter for this interface. +> `_addr`: Address being queried for the implementer of an interface. (If `_addr == 0` them `msg.sender` is assumed.) +> `_interfaceHash`: keccak256 hash of the name of the interface as a string. E.g. `web3.utils.keccak256('ERC777Token')` +> **returns:** The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0x0` if `_addr` did not register an implementer for this interface. ### Interface Implementation (`ERC820ImplementerInterface`) @@ -705,10 +750,12 @@ bytes32 constant ERC820_ACCEPT_MAGIC = keccak256("ERC820_ACCEPT_MAGIC"); ### Manager -The manager of an address (regular account or a contract) is the only entity allowed to register implementations of interfaces for the address. By default any address is its own manager. +The manager of an address (regular account or a contract) is the only entity allowed to register implementations of interfaces for the address. By default, any address is its own manager. The manager can transfer its role to another address by calling `setManager` with the address for which to transfer the manager and the address of the new manager. +**`setManager` function** + ``` solidity function setManager(address _addr, address _newManager) public ``` @@ -721,12 +768,24 @@ If `_addr` is `0x0`, `msg.sender` is assumed for `_addr`. If `_newManager` is `0x0`, the manager is reset to `_addr` itself as the manager. > **parameters** -> `_addr` Address for which to set the new manager. (Pass `0x0` to use `msg.sender` as the address.) -> `_newManager` The address of the new manager for `_addr`. (Pass `0x0` to reset the manager to `_addr`.) +> `_addr`: Address for which to set the new manager. (Pass `0x0` to use `msg.sender` as the address.) +> `_newManager`: The address of the new manager for `_addr`. (Pass `0x0` to reset the manager to `_addr`.) -## Backwards Compatibility +**`getManager` function** -This standard is backwards compatible with [ERC165], as both methods MAY be implemented without conflicting with each other. +``` solidity +function getManager(address _addr) public view returns(address) +``` + +Get the manager of an address. + +> **parameters** +> `_addr`: Address for which to return the manager. +> **returns:** Address of the manager for a given address. + +## Backward Compatibility + +This standard is backward compatible with [ERC165], as both methods MAY be implemented without conflicting with each other. ## Test Cases @@ -734,16 +793,19 @@ Please check the [jbaylina/eip820] repository for the full test suite. ## Implementation -The implementation can be found in this repo: [jbaylina/eip820]. +The implementation is available in the repo: [jbaylina/eip820]. ## Copyright Copyright and related rights waived via [CC0]. -[ERC155]: https://eips.ethereum.org/EIPS/eip-155 +[EIP155]: https://eips.ethereum.org/EIPS/eip-155 [ERC165]: https://eips.ethereum.org/EIPS/eip-165 [ERC672]: https://github.com/ethereum/EIPs/issues/672 [ERC820]: https://eips.ethereum.org/EIPS/eip-820 [ERC820 registry smart contract]: https://github.com/jbaylina/eip820/blob/master/contracts/ERC820Registry.sol +[manager]: #manager +[lookup]: #get-an-implementation-of-an-interface-for-an-address +[Nick's article]: https://medium.com/@weka/how-to-send-ether-to-11-440-people-187e332566b7 [jbaylina/eip820]: https://github.com/jbaylina/eip820 [CC0]: https://creativecommons.org/publicdomain/zero/1.0/ [Nick]: https://github.com/Arachnid/ From b20415cb9da86b535f3945c9daef2837612c53ed Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Tue, 28 Aug 2018 20:31:03 +0200 Subject: [PATCH 129/177] Automatically merged updates to draft EIP(s) 777 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-777.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/EIPS/eip-777.md b/EIPS/eip-777.md index f0fdac84..49ccbecc 100644 --- a/EIPS/eip-777.md +++ b/EIPS/eip-777.md @@ -111,9 +111,10 @@ function totalSupply() public view returns (uint256) Get the total number of minted tokens. -The total supply MUST be equal to the sum of the balances of all addresses—as returned by the `balanceOf` function. +*NOTE*: The total supply MUST be equal to the sum of the balances of all addresses—as returned by the `balanceOf` function. + +*NOTE*: The total supply MUST be equal to the sum of all the minted tokens as defined in all the `Minted` events minus the sum of all the burned tokens as defined in all the `Burned` events. (This applies as well to [tokens minted when the token contract is created][initial supply].) -The total supply MUST be equal to the sum of all the minted tokens as defined in all the `Minted` events minus the sum of all the burned tokens as defined in all the `Burned` events. One exception is a cloned token (with cloned balances) where the newer token contract MAY have a `totalSupply` greater than the difference of all the `Minted` and `Burned` events. > **returns:** Total supply of tokens currently in circulation. @@ -157,7 +158,7 @@ The following rules MUST be applied regarding the *granularity*: *NOTE*: [`defaultOperators`][defaultOperators] and [`isOperatorFor`][isOperatorFor] are also `view` functions, defined under the [operators] for consistency. *[ERC20] compatibility requirement*: -The decimals of the token MUST always be `18`. For a *pure* ERC777 token the [ERC20] `decimal` function is OPTIONAL, and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of `18` is implied.) For an [ERC20] compatible token, the `decimal` function is REQUIRED and MUST return `18`. (In [ERC20], the `decimals` function is OPTIONAL, but the value if the function is not present, is not clearly defined and may be assumed to be `0`. Hence for compatibility reasons, `decimals` MUST be implemented for [ERC20] compatible tokens.) +The decimals of the token MUST always be `18`. For a *pure* ERC777 token the [ERC20] `decimal` function is OPTIONAL, and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of `18` is implied.) For an [ERC20] compatible token, the `decimal` function is REQUIRED and MUST return `18`. (In [ERC20], the `decimals` function is OPTIONAL. If the function is not present, the `decimals` value is not clearly defined and may be assumed to be `0`. Hence for compatibility reasons, `decimals` MUST be implemented for [ERC20] compatible tokens.) *[ERC20] compatibility requirement*: The `name`, `symbol`, `totalSupply`, and `balanceOf` `view` functions MUST be backward compatible with [ERC20]. @@ -402,7 +403,8 @@ The token contract MUST `revert` when minting in any of the following cases: - The *recipient* is a contract, and it does not implement the `ERC777TokensRecipient` interface via [ERC820]. - The address of the *recipient* is `0x0`. -*NOTE*: The initial token supply at the creation of the token contract MUST be considered as minting for the amount of the initial supply to the address (or addresses) receiving the initial supply. +
    +*NOTE*: The initial token supply at the creation of the token contract MUST be considered as minting for the amount of the initial supply to the address(es) receiving the initial supply. This means one or more `Minted` events must be emitted and the `tokensReceived` hook of the recipient(s) MUST be called. *[ERC20] compatibility requirement*: While a `Sent` event MUST NOT be emitted when minting, if the token contract is [ERC20] backward compatible, a `Transfer` event with the `from` parameter set to `0x0` SHOULD be emitted as defined in the [ERC20] standard. @@ -619,7 +621,7 @@ Notify a send or mint (if `from` is `0x0`) of `amount` tokens from the `from` ad > `from`: *token holder* for a send and `0x` for a mint. > `to`: *token recipient*. > `amount`: Number of tokens the *recipient* balance is increased by. -> `data`: Extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint,. +> `data`: Extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint. > `operatorData`: Extra information provided by the address which triggered the balance increase. The following rules apply when calling the `tokensToSend` hook: @@ -759,6 +761,7 @@ Copyright and related rights waived via [CC0]. [sent]: #sent [minted]: #minted [burned]: #burned +[initial supply]: #initialSupply [logos]: https://github.com/ethereum/EIPs/tree/master/assets/eip-777/logo [beige logo]: ../assets/eip-777/logo/png/ERC-777-logo-beige-48px.png From 37446d6a39455b1b35b3a6bfa6d1a7e31a5c045b Mon Sep 17 00:00:00 2001 From: atlanticcrypto <32847326+atlanticcrypto@users.noreply.github.com> Date: Wed, 29 Aug 2018 12:06:56 -0400 Subject: [PATCH 130/177] Automatically merged updates to draft EIP(s) 1295 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1295.md | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/EIPS/eip-1295.md b/EIPS/eip-1295.md index 5952b0fd..9ced783c 100644 --- a/EIPS/eip-1295.md +++ b/EIPS/eip-1295.md @@ -1,6 +1,6 @@ --- eip: 1295 -title: Modify Ethereum PoW Incentive Structure and Diffuse Difficulty Bomb +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 @@ -12,25 +12,30 @@ created: 2018-08-05 ## Simple Summary -Network security and overall ecosystem maturity warrants the continued incentivization of Proof of Work participation but allows for a reduction in ancillary ETH issuance and the removal of the Difficulty Bomb. This EIP proposes a reduction of Uncle and removal of Nephew rewards while diffusing and removing the Difficulty Bomb with the Constantinople hard fork. +Network security and overall ecosystem maturity warrants the continued incentivization of Proof of Work participation but may allow for a reduction in ancillary ETH issuance and the delay of the Difficulty Bomb. This EIP proposes a reduction of Uncle and removal of Nephew rewards while delaying the Difficulty Bomb with the Constantinople hard fork. ## Abstract -Starting with CNSTNTNPL_FORK_BLKNUM the client will calculate the difficulty without the additional exponential component. Furthermore, Uncle rewards will be adjusted and Nephew rewards will be removed to eliminate excess ancillary ETH issuance. The current ETH block reward of 3 ETH will remain constant. +Starting with CNSTNTNPL_FORK_BLKNUM the client will calculate the difficulty based on a fake block number suggesting the client that the difficulty bomb is adjusting around 6 million blocks later than previously specified with the Homestead fork. + +Furthermore, Uncle rewards will be adjusted and Nephew rewards will be removed to eliminate excess ancillary ETH issuance. The current ETH block reward of 3 ETH will remain constant. ## Motivation -Network scalability and security are at the forefront of risks to the Ethereum protocol. With great strides being made towards on and off chain scalability, the existence of an artificial throughput limiting device in the protocol is no longer warranted. Removing the risk of reducing throughput through the initialization of the Difficulty Bomb is "low-hanging-fruit" to ensure continued operation at a minimum of current throughput into the future. +Network scalability and security are at the forefront of risks to the Ethereum protocol. With great strides being made towards on and off chain scalability, the existence of an artificial throughput limiting device in the protocol is not warranted. Removing the risk of reducing throughput through the initialization of the Difficulty Bomb is "low-hanging-fruit" to ensure continued operation at a minimum of current throughput through the next major hard fork (scheduled for late 2019). -The security layer of the Ethereum network is and should remain robust. Incentives for continued investment into the security of the growing ecosystem are paramount. At the same time, the ancillary issuance benefits of the Ethereum protocol can be adjusted to reduce the overall issuance profile. Aggressively adjusting Uncle and removing Nephew rewards will reduce the inflationary aspect of ETH issuance, while keeping the current block reward constant at 3 ETH will ensure top line incentives stay in place. +The security layer of the Ethereum network is and should remain robust. Incentives for continued operation of the security of the growing ecosystem are paramount. +At the same time, the ancillary issuance benefits of the Ethereum protocol can be adjusted to reduce the overall issuance profile. Aggressively adjusting Uncle and removing Nephew rewards will reduce the inflationary aspect of ETH issuance, while keeping the current block reward constant at 3 ETH will ensure top line incentives stay in place. ## Specification -#### Remove Difficulty Bomb -For the purposes of calc_difficulty, simply remove the exponential difficulty adjustment component, epsilon, i.e. the int(2**((block.number // 100000) - 2)). +#### 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: + fake_block_number = max(0, block.number - 6_000_000) if block.number >= CNSTNTNPL_FORK_BLKNUM else block.number + #### Adjust Uncle and Nephew rewards If an uncle is included in a block for `block.number >= CNSTNTNPL_FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is @@ -44,28 +49,38 @@ The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is This is a removal of all rewards for Nephew blocks. - ## Rationale -The Difficulty Bomb was instituted as a type of planned obsolescence to force the implementation of network upgrades with regular frequency. With the success of and resulting investment in the Ethereum protocol, these network upgrades have been secured through outright adoption and the size of the development community. With a result of the Difficulty Bomb being a reduction in effective network throughput and the risk of inaction inside the development community being reduced substantially, the original purpose of the Difficulty Bomb seems to have been diffused itself. With the focus on scalability for the protocol, eliminating a mechanism whereby the throughput of the network can be limited artificially, due to any circumstance, is a logical step to ensure continued minimum network operation at the current throughput level. + +The security layer of the Ethereum network is and should remain robust. Incentives for continued operation of the growing ecosystem’s security are paramount. + +At the same time, the ancillary issuance benefits of the Ethereum protocol can be adjusted to reduce the overall issuance profile. Aggressively adjusting Uncle and removing Nephew rewards will reduce the inflationary aspect of ETH issuance, while keeping the current block reward constant at 3 ETH will ensure top line incentives stay in place. + +The Difficulty Bomb was instituted as a type of planned obsolescence to force the implementation of network upgrades with regular frequency. With the focus on scalability for the protocol, delaying a mechanism whereby the throughput of the network can be limited artificially, due to any circumstance, is a logical step to ensure continued minimum network operation at the current throughput level. We believe the existence of the Difficulty Bomb has allowed for the inclusion of protocol upgrades in forced hard-forks, and will continue to do so. Through August 4th, the 2018 year to date reward issued to Uncle blocks totaled over 635,000 ETH. The average reward per Uncle totals 2.27 ETH. The year to date average Uncle rate is 22.49%. Using the year to date metrics, the ongoing average ETH paid as an Uncle reward per block is 0.51 ETH plus the Uncle inclusion reward of 0.021 ETH (0.09375 ETH * .2249), total Uncle related reward per block being over 0.53 ETH. With total year to date block rewards totaling approximately 3,730,000 ETH, the network has paid an additional 17pct in Uncle rewards. This is issuance that can be reduced while still maintaining the overall integrity and incentivization of network security. -Reducing the issuance of ETH in rewarding Uncle blocks (forked blocks caused by latency in propagation, a multi-faceted problem) should directly incentivize the investment in technologies and efficiencies to reduce the overall network Uncle rate which may lead to a reduction in Network wide Uncle rates, reducing ancillary issuance further. +Reducing the issuance of ETH in rewarding Uncle blocks (forked blocks caused by latency in propagation, a multi-faceted problem) should directly incentivize the investment in technologies and efficiencies to reduce block propagation latency which may lead to a reduction in Network wide Uncle rates, reducing ancillary issuance further. Reducing the Uncle rewards from the current specification to the proposed will yield two levels of ancillary ETH issuance for Uncles: Level 1 Uncle -> 0.75 ETH + Level 2 Uncle -> 0.375 ETH These levels are sufficient to continue incentivizing decentralized participation, while also providing an immediate economic incentive for the upgrade of the Ethereum node network and its tangential infrastructure. +We believe that the ETH network has been subsidizing transaction inclusion through the robust Uncle Reward structure since inception. We also believe that a removal of the set subsidy will create a dynamic response mechanism whereby Miners and transaction senders will minimize total costs of transaction inclusion. This dynamic response structure may limit unnecessary layer 1 transaction throughput while providing incentives for layer 2 scaling solutions. + The Nephew reward structure should be entirely eliminated. +Due to current market conditions, and the likelihood of a further USD denominated price decline (50%), we believe that any top line reduction to security incentives will put the Ethereum network at undue risk. Unlike the time of the Byzantium hard fork, current USD denominated economics for securing the Ethereum network threaten the participation of the most decentralized Miner community (at home miners), which we believe make up the largest proportion of the overall network hashrate. We believe eliminating this portion of the community will increase centralization and the probability of an organized network attack. + +For a technology so new and with so much potential, we find it extremely irresponsible to increase the likelihood of a network attack by reducing ETH Issuance past this proposal’s level. + With a reduction to the Uncle and removal of the Nephew reward, ancillary ETH issuance should drop (under normal market conditions, i.e. 22.49% uncle rate) by over 75pct and total ETH issuance from the successful sealing and mining of valid blocks should drop over 10pct. -Paired with the diffusal and removal of the Difficulty Bomb, this proposal strikes a balance between ensuring status quo network throughput, reducing overall ETH issuance, and continuing top line incentives for investment in infrastructure and efficiencies to continue expanding the Ethereum protocol's operational security. - +Paired with the diffusal of the Difficulty Bomb, this proposal strikes a balance between ensuring status quo network throughput, reducing overall ETH issuance, and continuing top line incentives for investment in infrastructure and efficiencies to continue operating the Ethereum network’s security layer. ## Backwards Compatibility From 7dbcbc35e4fd859a3d2b5b5c51bee805beaee42f Mon Sep 17 00:00:00 2001 From: Afri Schoedon <5chdn@users.noreply.github.com> Date: Fri, 31 Aug 2018 17:16:04 +0200 Subject: [PATCH 131/177] Move EIP-1234 to accepted; change delay to 12 months (#1365) --- EIPS/eip-1234.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/EIPS/eip-1234.md b/EIPS/eip-1234.md index 7317dc99..e9767df9 100644 --- a/EIPS/eip-1234.md +++ b/EIPS/eip-1234.md @@ -5,24 +5,24 @@ author: Afri Schoedon (@5chdn) discussions-to: https://ethereum-magicians.org/t/eip-1234-constantinople-difficulty-bomb-delay-and-block-reward-adjustment/833 type: Standards Track category: Core -status: Draft +status: Accepted created: 2018-07-19 --- ## Simple Summary -The average block times are increasing due to the difficulty bomb (also known as the "_ice age_") slowly accelerating. This EIP proposes to delay the difficulty bomb for approximately one and a half year and to reduce the block rewards with the Constantinople fork, the second part of the Metropolis fork. +The average block times are increasing due to the difficulty bomb (also known as the "_ice age_") slowly accelerating. This EIP proposes to delay the difficulty bomb for approximately 12 months and to reduce the block rewards with the Constantinople fork, the second part of the Metropolis fork. ## Abstract -Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty based on a fake block number suggesting the client that the difficulty bomb is adjusting around 6 million blocks later than previously specified with the Homestead fork. Furthermore, block rewards will be adjusted to a base of 2 ETH, uncle and nephew rewards will be adjusted accordingly. +Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty based on a fake block number suggesting the client that the difficulty bomb is adjusting around 5 million blocks later than previously specified with the Homestead fork. Furthermore, block rewards will be adjusted to a base of 2 ETH, uncle and nephew rewards will be adjusted accordingly. ## Motivation -The Casper development and switch to proof-of-stake is delayed, the Ethash proof-of-work should be feasible for miners and allow sealing new blocks every 15 seconds on average for another one and a half years. With the delay of the ice age, there is a desire to not suddenly also increase miner rewards. The difficulty bomb has been known about for a long time and now it's going to stop from happening. In order to maintain stability of the system, a block reward reduction that offsets the ice age delay would leave the system in the same general state as before. Reducing the reward also decreases the likelihood of a miner driven chain split as Ethereum approaches proof-of-stake. +The Casper development and switch to proof-of-stake is delayed, the Ethash proof-of-work should be feasible for miners and allow sealing new blocks every 15 seconds on average for another 12 months. With the delay of the ice age, there is a desire to not suddenly also increase miner rewards. The difficulty bomb has been known about for a long time and now it's going to stop from happening. In order to maintain stability of the system, a block reward reduction that offsets the ice age delay would leave the system in the same general state as before. Reducing the reward also decreases the likelihood of a miner driven chain split as Ethereum approaches proof-of-stake. ## 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: - fake_block_number = max(0, block.number - 6_000_000) if block.number >= CNSTNTNPL_FORK_BLKNUM else block.number + fake_block_number = max(0, block.number - 5_000_000) if block.number >= CNSTNTNPL_FORK_BLKNUM else block.number #### Adjust Block, Uncle, and Nephew rewards To ensure a constant Ether issuance, adjust the block reward to `new_block_reward`, where @@ -44,9 +44,9 @@ The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is This is the existing pre-Constantinople formula for nephew rewards, simply adjusted with `new_block_reward`. ## Rationale -This will delay the ice age by 42 million seconds (approximately 1.4 years), so the chain would be back at 30 second block times in summer 2020. An alternate proposal was to add special rules to the difficulty calculation to effectively _pause_ the difficulty between different blocks. This would lead to similar results. +This will delay the ice age by 29 million seconds (approximately 12 months), so the chain would be back at 30 second block times in winter 2019. An alternate proposal was to add special rules to the difficulty calculation to effectively _pause_ the difficulty between different blocks. This would lead to similar results. -This was previously discussed at All Core Devs Meeting [#42](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2042.md) without stating any details. This draft can be used to discuss further changes to the difficulty bomb and issuance in subsequent meetings. This EIP-1234 opposes directly the intent of [EIP-1227](https://github.com/ethereum/EIPs/issues/1227) which should be also considered in discussions. +This was previously discussed at All Core Devs Meeting [#42](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2042.md) and subsequent meetings; and accepted in the Constantinople Session [#1](https://github.com/ethereum/pm/issues/55). ## Backwards Compatibility This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, _Constantinople_. From 2df99653a043e2327d1030dded14aecef8c3527e Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Sun, 2 Sep 2018 20:02:56 -0400 Subject: [PATCH 132/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 111 ++++++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index dbf7047b..16d5a02a 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -11,17 +11,39 @@ created: 2018-05-04 ## Simple summary -This proposal describes a way for DOM environments to expose an Ethereum provider API that requires user approval. +This proposal describes a way for DOM environments to expose an Ethereum provider that requires user approval. ## Abstract -The previous generation of Ethereum-enabled DOM environments follows a pattern of directly injecting a provider object into the DOM without user consent. This exposes users of such environments to fingerprinting attacks since untrusted websites can check for the injected provider and reliably identify Ethereum-enabled clients. +The previous generation of Ethereum-enabled DOM environments follows a pattern of injecting a fully-enabled provider into the DOM without user consent. This puts users of such environments at risk because malicious websites can use this provider to view account information and to arbitrarily initiate unwanted Ethereum transactions on a user's behalf. -This proposal outlines a protocol in which dapps request access to an Ethereum provider API. +This proposal outlines a protocol in which DOM environments expose a read-only provider until full provider access is approved by the user. ## Specification -### Typical dapp initialization +### Definitions + +1. **Read-only provider** + + A read-only provider has no populated accounts and any RPC request that requires an account will fail. + +2. **Full provider** + + A full provider has populated accounts and any RPC request that requires an account will succeed. + +3. **`Provider#enable`** + + Providers exposed by DOM environments define a new `enable` method that returns a Promise. Calling this method triggers a user interface that allows the user to approve or deny full provider access for a given dapp. The returned Promise is resolved if the user approves full provider access or rejected if the user denies full provider access. + + ```js + ethereum.enable(): Promise + ``` + +### Protocol + +DOM environments expose a read-only provider globally at `window.ethereum` by default. Before initiating any RPC request that requires an account, like `eth_sendTransaction`, dapps must request a full provider by calling a new provider method, `ethereum.enable()`. This method triggers a user interface that allows the user to approve or deny full provider access for a given dapp. If the user approves full provider access, the provider at `window.ethereum` is populated with accounts and fully-enabled; if the user denies full provider access, the provider at `window.ethereum` is left unchanged. + +#### Typical dapp initialization ``` START dapp @@ -31,62 +53,71 @@ IF web3 is undefined STOP dapp ``` -### Proposed dapp initialization +#### Proposed dapp initialization ``` START dapp -REQUEST[1] provider -IF user approves - RESPOND[2] with provider - CONTINUE dapp -IF user rejects -IF non-Ethereum environment - NOOP[3] +IF provider is defined + ENABLE[1] full provider + IF user approves + RESOLVE[2] full provider + CONTINUE dapp + IF user denies + REJECT[3] with error + STOP dapp +IF provider is undefined + STOP dapp ``` -#### `[1] REQUEST` +##### `[1] ENABLE` -Dapps MUST request an Ethereum provider API by sending a message using the [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. This message MUST be sent with a payload object containing a `type` property with a value of "ETHEREUM_PROVIDER_REQUEST" and an optional `id` property corresponding to an identifier of a specific wallet provider, such as "METAMASK". +Dapps MUST request a full provider by calling the `enable` method on the default read-only provider. This method MUST trigger a user interface that allows the user to approve or deny full provider access for a given dapp. This method MUST return a Promise that is resolved if the user approves full provider access or rejected if the user denies full provider access. -#### `[2] RESPOND` +##### `[2] RESOLVE` -Ethereum-enabled DOM environments MUST respond with an Ethereum provider API by emitting an "ethereumprovider" [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent) on the `window` object. This custom event MUST pass a provider API as an `ethereum` property on its `detail` data object. +If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved. -#### `[3] NOOP` +##### `[3] REJECT` -If a user rejects access to the Ethereum provider API on an untrusted site, the site itself MUST NOT be notified in any way; notification of a rejection would allow third-party tools to still identify that a client is Ethereum-enabled despite not being granted access to any provider API. +If a user denies full provider access, the Promise returned when calling the `enable` method MUST be rejected with an informative Error. -### Example implementation: `postMessage` - -The following example demonstrates one possible implementation of this strategy in a browser-based DOM environment. Note that Ethereum-enabled environments on other platforms would most likely use platform-specific native messaging protocols, not `postMessage`. +### Example initialization ```js -window.addEventListener('load', () => { - // Listen for provider response - window.addEventListener('ethereumprovider', async ({ detail: { ethereum } }) => { - // Provider API exposed, continue - const networkVersion = await ethereum.send('net_version', []); - }); - // Request provider - window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, '*'); +window.addEventListener('load', async () => { + // Read-only provider is exposed by default + console.log(await ethereum.send('net_version')); + try { + // Request full provider if needed + await ethereum.enable(); + // Full provider exposed + await ethereum.send('eth_sendTransaction', [/* ... */]); + } catch (error) { + // User denied full provider access + } }); ``` -## Rationale - -The pattern of provider auto-injection followed by the previous generation of Ethereum-enabled DOM environments failed to protect user privacy by allowing untrusted websites to uniquely identify Ethereum users. This proposal establishes a new pattern wherein dapps must request access to an Ethereum provider API. This protocol directly prevents fingerprinting attacks by giving users the ability to reject provider exposure on a given website. - ### Constraints -* A provider API MUST NOT be exposed to websites by default. -* Dapps MUST request a provider API if it does not exist. -* Users MUST be able to approve or reject provider API access. -* A provider API MUST be exposed to websites after user consent. -* Environments MAY continue auto-exposing a provider API if users can opt-out. +* Browsers MUST expose a read-only provider at `window.ethereum` by default. +* Browsers MUST NOT expose a full provider globally by default. +* Dapps MUST request access to a full provider. +* Users MUST be able to approve or deny full provider access. +* A full provider MUST be exposed at `window.ethereum` after user approval. +* Dapps MUST be notified of user approval of full provider access. +* Dapps MUST be notified of user denial of full provider access. + +## Rationale + +The pattern of full provider auto-injection followed by the previous generation of Ethereum-enabled DOM environments fails to protect user privacy and fails to maintain safe user experience: untrusted websites can both view account information and arbitrarily initiate transactions on a user's behalf. Even though most users may reject unsolicited transactions on untrusted websites, a protocol for provider exposure should make such unsolicited requests impossible. + +This proposal establishes a new pattern wherein dapps must request access to a full Ethereum provider. This protocol directly strengthens user privacy by hiding user accounts and preventing unsolicited transaction requests on untrusted sites. ### Immediate value-add -* Users can reject provider API access on untrusted sites to prevent fingerprinting. +* Users can reject full provider access on untrusted sites to hide accounts. +* Users can reject full provider access on untrusted sites to prevent unsolicited transactions. ### Long-term value-add @@ -97,7 +128,7 @@ The pattern of provider auto-injection followed by the previous generation of Et ## Backwards compatibility -This proposal impacts dapp authors and requires that they request access to an Ethereum provider API before using it. This proposal also impacts developers of Ethereum-enabled environments or dapp browsers as these tools should no longer auto-expose any provider API; instead, they should only do so if a website requests a provider API and if the user consents to its access. Environments may continue to auto-expose an Ethereum provider API as long as users have the ability to disable this behavior. +This proposal impacts dapp authors and requires that they request access to a full Ethereum provider before using it to initiate any RPC call that requires an account. This proposal also impacts developers of Ethereum-enabled DOM environments or dapp browsers as these tools should no longer auto-expose a full provider populated with accounts; instead, they should expose a read-only provider and only expose a full provider if a website requests one and a user consents to its access. ## Implementation From a5d2f32c51d7d1f21a59c7fe18ff7c179701aa08 Mon Sep 17 00:00:00 2001 From: yarrumretep Date: Mon, 3 Sep 2018 03:36:28 -0400 Subject: [PATCH 133/177] Moving EIP-1167 to Final status (#1373) * Moving EIP-1167 to Last Call status * included code-golf gas improvements, eliminated extraneous tooling (factory, probe, deployment bytecode), added vanity address optimization * adding last call comment period end date * adding last call comment period end date * moved status to Final * removed Last Call period end date --- EIPS/eip-1167.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md index 0c851bf5..d0a189ce 100644 --- a/EIPS/eip-1167.md +++ b/EIPS/eip-1167.md @@ -3,14 +3,13 @@ eip: 1167 title: Minimal Proxy Contract author: Peter Murray (@yarrumretep), Nate Welch (@flygoing), Joe Messerman (@JAMesserman) discussions-to: https://github.com/optionality/clone-factory/issues/10 -status: Last Call +status: Final type: Standards Track category: ERC created: 2018-06-22 --- -### Last Call Comment Period Ends August 27, 2018 @ 11PM UTC ## Simple Summary From 0ff622087232ba9ef43f621225b1695b7b85213b Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Tue, 4 Sep 2018 10:02:18 -0400 Subject: [PATCH 134/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 16d5a02a..5e9a2189 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -71,11 +71,11 @@ IF provider is undefined ##### `[1] ENABLE` -Dapps MUST request a full provider by calling the `enable` method on the default read-only provider. This method MUST trigger a user interface that allows the user to approve or deny full provider access for a given dapp. This method MUST return a Promise that is resolved if the user approves full provider access or rejected if the user denies full provider access. +Dapps MUST request a full provider by calling the `enable` method on the default read-only provider. This method MUST trigger a user interface that allows the user to approve or deny full provider access for a given dapp. This method MUST return a Promise that is resolved with an array of the user's public keys if the user approves full provider access or rejected if the user denies full provider access. ##### `[2] RESOLVE` -If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved. +If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved with an array of the user's public keys. ##### `[3] REJECT` From cc61e826d79fadfb980f280c10ffc3480f5ff4c6 Mon Sep 17 00:00:00 2001 From: Alan Lu Date: Wed, 5 Sep 2018 12:02:20 -0500 Subject: [PATCH 135/177] Automatically merged updates to draft EIP(s) 1154 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1154.md | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/EIPS/eip-1154.md b/EIPS/eip-1154.md index 51651576..a9fedf4d 100644 --- a/EIPS/eip-1154.md +++ b/EIPS/eip-1154.md @@ -34,7 +34,7 @@ Both the ID and the results are intentionally unstructured so that things like t
    Oracle
    An entity which reports data to the blockchain.
    -
    Oracle handler
    +
    Oracle consumer
    A smart contract which receives data from an oracle.
    ID
    @@ -44,12 +44,12 @@ Both the ID and the results are intentionally unstructured so that things like t
    Data associated with an id which is reported by an oracle. This data oftentimes will be the answer to a question tied to the id. Other equivalent terms that have been used include: answer, data, outcome.
    Report
    -
    A pair (ID, result) which an oracle sends to an oracle handler.
    +
    A pair (ID, result) which an oracle sends to an oracle consumer.
    ```solidity -interface OracleHandler { - function receiveResult(bytes32 id, bytes32 result) external; +interface OracleConsumer { + function receiveResult(bytes32 id, bytes result) external; } ``` @@ -57,21 +57,23 @@ interface OracleHandler { `receiveResult` MUST revert if `receiveResult` has been called with the same `id` before. -`receiveResult` MAY revert if the `id` or `result` cannot be handled by the handler. +`receiveResult` MAY revert if the `id` or `result` cannot be handled by the consumer. + +Consumers MUST coordinate with oracles to determine how to encode/decode results to and from `bytes`. For example, `abi.encode` and `abi.decode` may be used to implement a codec for results in Solidity. `receiveResult` SHOULD revert if the consumer receives a unexpected result format from the oracle. The oracle can be any Ethereum account. ## Rationale The specs are currently very similar to what is implemented by ChainLink (which can use any arbitrarily-named callback) and Oraclize (which uses `__callback`). -With this spec, the oracle _pushes_ state to the handler, which must react accordingly to the updated state. An alternate _pull_-based interface can be prescribed, as follows: +With this spec, the oracle _pushes_ state to the consumer, which must react accordingly to the updated state. An alternate _pull_-based interface can be prescribed, as follows: ### Alternate Pull-based Interface Here are alternate specs loosely based on Gnosis prediction market contracts v1. Reality Check also exposes a similar endpoint (`getFinalAnswer`). ```solidity interface Oracle { - function resultFor(bytes32 id) external view returns (bytes32 result); + function resultFor(bytes32 id) external view returns (bytes result); } ``` @@ -80,25 +82,29 @@ interface Oracle { `resultFor` MUST return the same result for an `id` after that result is available. ### Push vs Pull -Note that push-based interfaces may be adapted into pull-based interfaces. Simply deploy an oracle handler which stores the result received and implements `resultFor` accordingly. +Note that push-based interfaces may be adapted into pull-based interfaces. Simply deploy an oracle consumer which stores the result received and implements `resultFor` accordingly. -Similarly, every pull-based system can be adapted into a push-based system: just add a method on the oracle smart contract which takes an oracle handler address and calls `receiveResult` on that address. +Similarly, every pull-based system can be adapted into a push-based system: just add a method on the oracle smart contract which takes an oracle consumer address and calls `receiveResult` on that address. In both cases, an additional transaction would have to be performed, so the choice to go with push or pull should be based on the dominant use case for these oracles. In the simple case where a single account has the authority to decide the outcome of an oracle question, there is no need to deploy an oracle contract and store the outcome on that oracle contract. Similarly, in the case where the outcome comes down to a vote, existing multisignature wallets can be used as the authorized oracle. -#### Multiple Oracle Handlers -In the case that many oracle handlers depend on a single oracle result and all these handlers expect the result to be pushed to them, the push and pull adaptations mentioned before may be combined if the pushing oracle cannot be trusted to send the same result to every handler (in a sense, this forwards the trust to the oracle adaptor implementation). +#### Multiple Oracle Consumers +In the case that many oracle consumers depend on a single oracle result and all these consumers expect the result to be pushed to them, the push and pull adaptations mentioned before may be combined if the pushing oracle cannot be trusted to send the same result to every consumer (in a sense, this forwards the trust to the oracle adaptor implementation). -In a pull-based system, each of the handlers would have to be called to pull the result from the oracle contract, but in the proposed push-based system, the adapted oracle would have to be called to push the results to each of the handlers. +In a pull-based system, each of the consumers would have to be called to pull the result from the oracle contract, but in the proposed push-based system, the adapted oracle would have to be called to push the results to each of the consumers. -Transaction-wise, both systems are roughly equivalent in efficiency in this scenario, but in the push-based system, there's a need for the oracle handlers to store the results again, whereas in the pull-based system, the handlers may continue to refer to the oracle for the results. Although this may be somewhat less efficient, requiring the handlers to store the results can also provide security guarantees, especially with regards to result immutability. +Transaction-wise, both systems are roughly equivalent in efficiency in this scenario, but in the push-based system, there's a need for the oracle consumers to store the results again, whereas in the pull-based system, the consumers may continue to refer to the oracle for the results. Although this may be somewhat less efficient, requiring the consumers to store the results can also provide security guarantees, especially with regards to result immutability. #### Result Immutability -In both the proposed specification and the alternate specification, results are immutable once they are determined. This is due to the expectation that typical handlers will require results to be immutable in order to determine a resulting state consistently. With the proposed push-based system, the handler enforces the result immutability requirement, whereas in the alternate pull-based system, either the oracle would have to be trusted to implement the spec correctly and enforce the immutability requirement, or the handler would also have to handle result immutability. +In both the proposed specification and the alternate specification, results are immutable once they are determined. This is due to the expectation that typical consumers will require results to be immutable in order to determine a resulting state consistently. With the proposed push-based system, the consumer enforces the result immutability requirement, whereas in the alternate pull-based system, either the oracle would have to be trusted to implement the spec correctly and enforce the immutability requirement, or the consumer would also have to handle result immutability. For data which mutates over time, the `id` field may be structured to specify "what" and "when" for the data (using 128 bits to specify "when" is still safe for many millenia). +## Implementation + +* [Tidbit](https://github.com/levelkdev/tidbit) tracks this EIP. + ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From a3384c8841f959e61c11a53c9bae684dc461a99a Mon Sep 17 00:00:00 2001 From: Ryan Ghods Date: Mon, 10 Sep 2018 05:41:17 -1000 Subject: [PATCH 136/177] Automatically merged updates to draft EIP(s) 1193 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1193.md | 227 ++++++++++++++++++++++++++++------------------- 1 file changed, 138 insertions(+), 89 deletions(-) diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md index afbe3930..bfe09bb4 100644 --- a/EIPS/eip-1193.md +++ b/EIPS/eip-1193.md @@ -14,12 +14,28 @@ requires: 1102 This EIP formalizes an Ethereum Provider JavaScript API for consistency across clients and applications. -The provider is designed to be minimal, containing 3 methods: `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`. +The provider is designed to be minimal, containing 4 methods: `enable`, `send`, `subscribe`, and `unsubscribe`. It emits 4 types of events: `connect`, `close`, `networkChanged`, and `accountsChanged`. + +It is intended to be available on `window.ethereum`. ## API +### Enable + +By default a "read-only" provider is supplied to allow access to the blockchain while preserving user privacy. + +A full provider can be requested to allow account-level methods: + +```js +ethereum.enable(): Promise<[String]>; +``` + +Promise resolves with an array of the accounts' public keys, or rejects with `Error`. + ### Send +Ethereum API methods can be sent and received: + ```js ethereum.send(method: String, params?: Array): Promise; ``` @@ -33,7 +49,7 @@ See the [available methods](https://github.com/ethereum/wiki/wiki/JSON-RPC#json- #### Subscribe ```js -ethereum.subscribe(subscriptionType: String, params?: Array): Promise; +ethereum.subscribe(subscriptionType: String, params?: Array): Promise; ``` Promise resolves with `subscriptionId: String` or rejects with `Error`. @@ -51,7 +67,7 @@ The event emits with `result`, the subscription `result` or an `Error` object. #### Unsubscribe ```js -ethereum.unsubscribe(subscriptionId: String): Promise; +ethereum.unsubscribe(subscriptionId: String): Promise; ``` Promise resolves with `success: Boolean` or rejects with `Error`. @@ -117,113 +133,131 @@ ethereum.constructor.name; ## Examples ```js -// Request Ethereum Provider (EIP 1102) -window.addEventListener('message', event => { - if (event.data && event.data.type === 'ETHEREUM_PROVIDER_SUCCESS') { - start(window.ethereum); - } -}); -window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, this.origin); +const ethereum = window.ethereum; -function start(ethereum) { - // A) Primary use case - set provider in web3.js - web3.setProvider(ethereum); +// A) Primary use case - set provider in web3.js +web3.setProvider(ethereum); - // B) Secondary use case - use provider object directly - // Example: Log accounts - ethereum - .send('eth_accounts') - .then(accounts => { - console.log(`Accounts:\n${accounts.join('\n')}`); - }) - .catch(error => { - console.error( - `Error fetching accounts: ${error.message}. - Code: ${error.code}. Data: ${error.data}` - ); - }); +// B) Secondary use case - use provider object directly +// Example 1: Log last block +ethereum + .send('eth_getBlockByNumber', ['latest', 'true']) + .then(block => { + console.log(`Block ${block.number}:\n${block}`); + }) + .catch(error => { + console.error( + `Error fetching last block: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); - // Example: Log last block - ethereum - .send('eth_getBlockByNumber', ['latest', 'true']) - .then(block => { - console.log(`Block ${block.number}:\n${block}`); - }) - .catch(error => { - console.error( - `Error fetching last block: ${error.message}. - Code: ${error.code}. Data: ${error.data}` - ); - }); +// Example 2: Enable full provider +ethereum + .enable() + .then(accounts => { + console.log(`Enabled accounts:\n${accounts.join('\n')}`); + }) + .catch(error => { + console.error( + `Error enabling provider: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); - // Example: Log new blocks - let subId; - ethereum - .subscribe('newHeads') - .then(subscriptionId => { - subId = subscriptionId; - ethereum.on(subscriptionId, block => { - if (result instanceOf Error) { - const error = result; - console.error( - `Error from newHeads subscription: ${error.message}. - Code: ${error.code}. Data: ${error.data}` - ); - } else { - console.log(`New block ${block.number}:\n${block}`); - } - }); - }) - .catch(error => { - console.error( - `Error making newHeads subscription: ${error.message}. - Code: ${error.code}. Data: ${error.data}` - ); - }); - // to unsubscribe - ethereum - .unsubscribe(subId) - .then(result => { - console.log(`Unsubscribed newHeads subscription ${subscriptionId}`); - }) - .catch(error => { - console.error( - `Error unsubscribing newHeads subscription: ${error.message}. - Code: ${error.code}. Data: ${error.data}` - ); - }); - - // Example: Log when accounts change - const logAccounts = accounts => { +// Example 3: Log available accounts +ethereum + .send('eth_accounts') + .then(accounts => { console.log(`Accounts:\n${accounts.join('\n')}`); - }; - ethereum.on('accountsChanged', logAccounts); - // to unsubscribe - ethereum.removeListener('accountsChanged', logAccounts); - - // Example: Log if connection ends - ethereum.on('close', (code, reason) => { - console.log( - `Ethereum provider connection closed: ${reason}. Code: ${code}` + }) + .catch(error => { + console.error( + `Error fetching accounts: ${error.message}. + Code: ${error.code}. Data: ${error.data}` ); }); } + +// Example 4: Log new blocks +let subId; +ethereum + .subscribe('newHeads') + .then(subscriptionId => { + subId = subscriptionId; + ethereum.on(subscriptionId, block => { + if (result instanceOf Error) { + const error = result; + console.error( + `Error from newHeads subscription: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + } else { + console.log(`New block ${block.number}:\n${block}`); + } + }); + }) + .catch(error => { + console.error( + `Error making newHeads subscription: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); +// to unsubscribe +ethereum + .unsubscribe(subId) + .then(result => { + console.log(`Unsubscribed newHeads subscription ${subscriptionId}`); + }) + .catch(error => { + console.error( + `Error unsubscribing newHeads subscription: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); + +// Example 5: Log when accounts change +const logAccounts = accounts => { + console.log(`Accounts:\n${accounts.join('\n')}`); +}; +ethereum.on('accountsChanged', logAccounts); +// to unsubscribe +ethereum.removeListener('accountsChanged', logAccounts); + +// Example 6: Log if connection ends +ethereum.on('close', (code, reason) => { + console.log( + `Ethereum provider connection closed: ${reason}. Code: ${code}` + ); +}); ``` ## Specification +### Enable + +The provider supplied to a new dapp **MUST** be a "read-only" provider: authenticating no accounts by default, returning a blank array for `eth_accounts`, and rejecting any methods that require an account. + +If the dapp has been previously authenticated and remembered by the user, then the provider supplied on load **MAY** automatically be enabled with the previously authenticated accounts. + +If no accounts are authenticated, the `enable` method **MUST** ask the user which account(s) they would like to authenticate to the dapp. If the request has been previously granted and remembered, the `enable` method **MAY** immediately return with the prior remembered accounts and permissions. + +The `enable` method **MUST** return a Promise, resolving with an array of the accounts' public keys, or rejecting with an `Error`. If the accounts enabled by provider change, the `accountsChanged` event **MUST** also emit. + ### Send The `send` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object). If the Ethereum JSON-RPC API returns a response object with no error, then the Promise **MUST** resolve with the `response.result` object untouched by the implementing Ethereum Provider. -If the Ethereum JSON-RPC API returns response object that contains an error property then the Promise **MUST** be rejected with an Error object containing the `response.error.message` as the Error message, `response.error.code` as a code property on the error and `response.error.data` as a data property on the error. +If the Ethereum JSON-RPC API returns response object that contains an error property then the Promise **MUST** reject with an Error object containing the `response.error.message` as the Error message, `response.error.code` as a code property on the error and `response.error.data` as a data property on the error. -If an error occurs during processing, such as an HTTP error or internal parsing error, then the Promise **MUST** be rejected with an Error object. +If an error occurs during processing, such as an HTTP error or internal parsing error, then the Promise **MUST** reject with an `Error` object. If the implementing Ethereum Provider is not talking to an external Ethereum JSON-RPC API provider then it **MUST** resolve with an object that matches the JSON-RPC API object as specified in the [Ethereum JSON-RPC documentation](https://github.com/ethereum/wiki/wiki/JSON-RPC). +If the JSON-RPC request requires an account that is not yet authenticated, the Promise **MUST** reject with an `Error`. + ### Subscriptions The `subscribe` method **MUST** send a properly formatted [JSON-RPC request](https://www.jsonrpc.org/specification#request_object) with method `eth_subscribe` and params `[subscriptionType: String, {...params: Array}]` and **MUST** return a Promise that resolves with `subscriptionId: String` or rejected with an Error object. @@ -262,7 +296,13 @@ The implementing Ethereum Provider **MUST** be compatible as a `web3.js` provide If an Error object is returned, it **MUST** contain a human readable string message describing the error and **SHOULD** populate the `code` and `data` properties on the error object with additional error details. -Appropriate error codes **SHOULD** follow the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). +Appropriate error codes **SHOULD** follow the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes), along with the following table: + +| Status code | Name | Description | +| ----------- | ------------------------- | -------------------------------------------------------------------------------------------------------------- | +| 4001 | User Denied Full Provider | User denied the enabling of the full Ethereum Provider by choosing not to authorize any accounts for the dapp. | +| | | | +| | | | ## Sample Class Implementation @@ -287,6 +327,15 @@ class EthereumProvider extends EventEmitter { /* Methods */ + enable() { + return new Promise((resolve, reject) => { + window.mist + .requestAccounts() + .then(resolve) + .catch(reject); + }); + } + send(method, params = []) { if (!method || typeof method !== 'string') { return new Error('Method is not a valid string.'); From 8ca90176802116451cbcb1c93a1a545a135501e9 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 13 Sep 2018 09:36:51 +0100 Subject: [PATCH 137/177] Draft EIP for ENS support for contract ABIs (#205) * Draft EIP for ENS support for contract ABIs * Fix signature for resolver function * Rename abi to ABI to avoid name clashes * Update and rename eip-ens-abi-lookup.md to eip-634.md * Update eip-634.md * Update eip-634.md --- EIPS/eip-634.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 EIPS/eip-634.md diff --git a/EIPS/eip-634.md b/EIPS/eip-634.md new file mode 100644 index 00000000..b1167773 --- /dev/null +++ b/EIPS/eip-634.md @@ -0,0 +1,69 @@ +--- +eip: 634 +title: ENS support for contract ABIs +author: Nick Johnson +type: Standards Track +category: ERC +status: Draft +created: 2017-02-06 +requires: 137, 181 +--- + +## Simple Summary +This EIP proposes a mechanism for storing ABI definitions in ENS, for easy lookup of contract interfaces by callers. + +## Abstract +ABIs are important metadata required for interacting with most contracts. At present, they are typically supplied out-of-band, which adds an additional burden to interacting with contracts, particularly on a one-off basis or where the ABI may be updated over time. The small size of ABIs permits an alternative solution, storing them in ENS, permitting name lookup and ABI discovery via the same process. + +ABIs are typically quite compact; the largest in-use ABI we could find, that for the DAO, is 9450 bytes uncompressed JSON, 6920 bytes uncompressed CBOR, and 1128 bytes when the JSON form is compressed with zlib. Further gains on CBOR encoding are possible using a CBOR extension that permits eliminating repeated strings, which feature extensively in ABIs. Most ABIs, however, are far shorter than this, consisting of only a few hundred bytes of uncompressed JSON. + +This EIP defines a resolver profile for retrieving contract ABIs, as well as encoding standards for storing ABIs for different applications, allowing the user to select between different representations based on their need for compactness and other considerations such as onchain access. + +## Specification +### ABI encodings +In order to allow for different tradeoffs between onchain size and accessibility, several ABI encodings are defined. Each ABI encoding is defined by a unique constant with only a single bit set, allowing for the specification of 256 unique encodings in a single uint. + +The currently recognised encodings are: + +| ID | Description | +|----|----------------------| +| 1 | JSON | +| 2 | zlib-compressed JSON | +| 4 | CBOR | +| 8 | URI | + +This table may be extended in future through the EIP process. + +Encoding type 1 specifies plaintext JSON, uncompressed; this is the standard format in which ABIs are typically encoded, but also the bulkiest, and is not easily parseable onchain. + +Encoding type 2 specifies zlib-compressed JSON. This is significantly smaller than uncompressed JSON, and is straightforward to decode offchain. However, it is impracticalfor onchain consumers to use. + +Encoding type 4 is [CBOR](http://cbor.io/). CBOR is a binary encoding format that is a superset of JSON, and is both more compact and easier to parse in limited environments such as the EVM. Consumers that support CBOR are strongly encouraged to also support the [stringref extension](http://cbor.schmorp.de/stringref) to CBOR, which provides significant additional reduction in encoded size. + +Encoding type 8 indicates that the ABI can be found elsewhere, at the specified URI. This is typically the most compact of the supported forms, but also adds external dependencies for implementers. The specified URI may use any schema, but HTTP, IPFS, and Swarm are expected to be the most common. + +### Resolver profile +A new resolver interface is defined, consisting of the following method: + + function ABI(bytes32 node, uint256 contentType) constant returns (uint256, bytes); + +The interface ID of this interface is 0x2203ab56. + +contentType is a bitfield, and is the bitwise OR of all the encoding types the caller will accept. Resolvers that implement this interface must return an ABI encoded using one of the requested formats, or `(0, "")` if they do not have an ABI for this function, or do not support any of the requested formats. + +The `abi` resolver profile is valid on both forward and reverse records. + +### ABI lookup process + +When attempting to fetch an ABI based on an ENS name, implementers should first attempt an ABI lookup on the name itself. If that lookup returns no results, they should attempt a reverse lookup on the Ethereum address the name resolves to. + +Implementers should support as many of the ABI encoding formats as practical. + +## Rationale + +Storing ABIs onchain avoids the need to introduce additional dependencies for applications wishing to fetch them, such as swarm or HTTP access. Given the typical compactness of ABIs, we believe this is a worthwhile tradeoff in many cases. + +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/). From e9f59fe9b934063ac92a84f4512091bf0f19eb6e Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 13 Sep 2018 09:41:11 +0100 Subject: [PATCH 138/177] Rename ENS ABI to eip-205 (#1406) * Draft EIP for ENS support for contract ABIs * Fix signature for resolver function * Rename abi to ABI to avoid name clashes * Update and rename eip-ens-abi-lookup.md to eip-634.md * Update eip-634.md * Update eip-634.md * Update and rename eip-634.md to eip-205.md * Delete eip-634.md --- EIPS/{eip-634.md => eip-205.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename EIPS/{eip-634.md => eip-205.md} (99%) diff --git a/EIPS/eip-634.md b/EIPS/eip-205.md similarity index 99% rename from EIPS/eip-634.md rename to EIPS/eip-205.md index b1167773..09c5e366 100644 --- a/EIPS/eip-634.md +++ b/EIPS/eip-205.md @@ -1,5 +1,5 @@ --- -eip: 634 +eip: 205 title: ENS support for contract ABIs author: Nick Johnson type: Standards Track From 3136c20095063d5eb4384c1ffa590f52419a2736 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Thu, 13 Sep 2018 14:10:14 -0400 Subject: [PATCH 139/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 5e9a2189..67420e6a 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -39,6 +39,14 @@ This proposal outlines a protocol in which DOM environments expose a read-only p ethereum.enable(): Promise ``` +4. **`Provider#isEnabled`** + + Providers exposed by DOM environments define a new `isEnabled` property that is set to `true` if the user approves full provider access or `false` if the user denies full provider access. + + ```js + ethereum.isEnabled: boolean + ``` + ### Protocol DOM environments expose a read-only provider globally at `window.ethereum` by default. Before initiating any RPC request that requires an account, like `eth_sendTransaction`, dapps must request a full provider by calling a new provider method, `ethereum.enable()`. This method triggers a user interface that allows the user to approve or deny full provider access for a given dapp. If the user approves full provider access, the provider at `window.ethereum` is populated with accounts and fully-enabled; if the user denies full provider access, the provider at `window.ethereum` is left unchanged. @@ -71,15 +79,15 @@ IF provider is undefined ##### `[1] ENABLE` -Dapps MUST request a full provider by calling the `enable` method on the default read-only provider. This method MUST trigger a user interface that allows the user to approve or deny full provider access for a given dapp. This method MUST return a Promise that is resolved with an array of the user's public keys if the user approves full provider access or rejected if the user denies full provider access. +Dapps MUST request a full provider by calling the `enable` method on the default read-only provider. This method MUST trigger a user interface that allows the user to approve or deny full provider access for a given dapp. This method MUST return a Promise that is resolved with an array of the user's public addresses if the user approves full provider access or rejected if the user denies full provider access. ##### `[2] RESOLVE` -If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved with an array of the user's public keys. +If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved with an array of the user's public addresses. `Provider#isEnabled` MUST be set to `true`. ##### `[3] REJECT` -If a user denies full provider access, the Promise returned when calling the `enable` method MUST be rejected with an informative Error. +If a user denies full provider access, the Promise returned when calling the `enable` method MUST be rejected with an informative Error. `Provider#isEnabled` MUST be set to `false`. ### Example initialization From 7c9b9d89e61776ff9b24d128fa43e6e3d65dbc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 31 Aug 2018 22:06:30 +0200 Subject: [PATCH 140/177] EIP-1355: Add Jean M. Cyr, fix a typo --- EIPS/eip-1355.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1355.md b/EIPS/eip-1355.md index cf567db5..94197b68 100644 --- a/EIPS/eip-1355.md +++ b/EIPS/eip-1355.md @@ -1,7 +1,7 @@ --- eip: 1355 title: Ethash 1a -author: Paweł Bylica +author: Paweł Bylica , Jean M. Cyr [@jean-m-cyr](https://github.com/jean-m-cyr) discussions-to: https://ethereum-magicians.org/t/eip-1355-ethash-1a/1167 status: Draft type: Standards Track @@ -21,7 +21,7 @@ Provide minimal set of changes to Ethash algorithm to hinder and delay the adopt return ((v1 ^ v2) * FNV1A_PRIME) % 2**32 ``` where `FNV1A_PRIME` is 16777499 or 16777639. -2. Change the hash function that determines the DAG item index in Ethash algorithm from `fnv() to new `fnv1a()`. +2. Change the hash function that determines the DAG item index in Ethash algorithm from `fnv()` to new `fnv1a()`. In [Main Loop](https://github.com/ethereum/wiki/wiki/Ethash#main-loop) change ```python p = fnv(i ^ s[0], mix[i % w]) % (n // mixhashes) * mixhashes From 63e0929de7a5350e54af1879b571936e5093c2f9 Mon Sep 17 00:00:00 2001 From: Paul Bouchon Date: Thu, 13 Sep 2018 21:07:58 -0400 Subject: [PATCH 141/177] Automatically merged updates to draft EIP(s) 1102 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-1102.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md index 67420e6a..38612d40 100644 --- a/EIPS/eip-1102.md +++ b/EIPS/eip-1102.md @@ -39,14 +39,6 @@ This proposal outlines a protocol in which DOM environments expose a read-only p ethereum.enable(): Promise ``` -4. **`Provider#isEnabled`** - - Providers exposed by DOM environments define a new `isEnabled` property that is set to `true` if the user approves full provider access or `false` if the user denies full provider access. - - ```js - ethereum.isEnabled: boolean - ``` - ### Protocol DOM environments expose a read-only provider globally at `window.ethereum` by default. Before initiating any RPC request that requires an account, like `eth_sendTransaction`, dapps must request a full provider by calling a new provider method, `ethereum.enable()`. This method triggers a user interface that allows the user to approve or deny full provider access for a given dapp. If the user approves full provider access, the provider at `window.ethereum` is populated with accounts and fully-enabled; if the user denies full provider access, the provider at `window.ethereum` is left unchanged. @@ -83,11 +75,11 @@ Dapps MUST request a full provider by calling the `enable` method on the default ##### `[2] RESOLVE` -If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved with an array of the user's public addresses. `Provider#isEnabled` MUST be set to `true`. +If a user approves full provider access, DOM environments MUST expose a fully-enabled provider at `window.ethereum` that is populated with accounts. The Promise returned when calling the `enable` method MUST be resolved with an array of the user's public addresses. ##### `[3] REJECT` -If a user denies full provider access, the Promise returned when calling the `enable` method MUST be rejected with an informative Error. `Provider#isEnabled` MUST be set to `false`. +If a user denies full provider access, the Promise returned when calling the `enable` method MUST be rejected with an informative Error. ### Example initialization From 7b3104cb4704deca7ccdf73207f15aec61ab7be0 Mon Sep 17 00:00:00 2001 From: Jacques Dafflon Date: Mon, 17 Sep 2018 23:08:27 +0200 Subject: [PATCH 142/177] Automatically merged updates to draft EIP(s) 820 Hi, I'm a bot! This change was automatically merged because: - It only modifies existing Draft or Last Call EIP(s) - The PR was approved or written by at least one author of each modified EIP - The build is passing --- EIPS/eip-820.md | 96 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/EIPS/eip-820.md b/EIPS/eip-820.md index 6b219b54..504ee09f 100644 --- a/EIPS/eip-820.md +++ b/EIPS/eip-820.md @@ -13,15 +13,15 @@ created: 2018-01-05 This standard defines a universal registry smart contract where any address (contract or regular account) can register which interface it supports and which smart contract is responsible for its implementation. -This standard keeps backwards compatibility with [ERC165]. +This standard keeps backward compatibility with [ERC165]. ## Abstract -This standard attempts to define a registry where smart contracts and regular accounts can publish which functionalities they implement—either directly or through a proxy contract . +This standard defines a registry where smart contracts and regular accounts can publish which functionalities they implement—either directly or through a proxy contract. -The rest of the world can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation. +Anyone can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation. -This registry MAY be deployed on any chain and will share the exact same address. +This registry MAY be deployed on any chain and shares the same address on all chains. Interfaces with zeroes (`0`) as the last 28 bytes are considered [ERC165] interfaces, and this registry SHALL forward the call to the contract to see if it implements the interface. @@ -29,11 +29,11 @@ This contract also acts as an [ERC165] cache to reduce gas consumption. ## Motivation -There has been different approaches to define pseudo-introspection in Ethereum. The first is [ERC165] which has the limitation that it cannot be used by regular accounts. The second attempt is [ERC672] which uses reverseENS. Using reverseENS has two issues. First, it is unnecessarily complex, and second, ENS is still a centralized contract controlled by a multisig. This multisig, theoretically would be able to modify the system. +There have been different approaches to define pseudo-introspection in Ethereum. The first is [ERC165] which has the limitation that it cannot be used by regular accounts. The second attempt is [ERC672] which uses reverse [ENS]. Using reverse [ENS] has two issues. First, it is unnecessarily complicated, and second, [ENS] is still a centralized contract controlled by a multisig. This multisig theoretically would be able to modify the system. -This standard is much simpler than [ERC672] and it is fully decentralized. +This standard is much simpler than [ERC672], and it is *fully* decentralized. -This standard also provides a unique address for all chains. Thus solving the problem of resolving the correct registry address for different chains. +This standard also provides a *unique* address for all chains. Thus solving the problem of resolving the correct registry address for different chains. ## Specification @@ -281,20 +281,22 @@ This contract is going to be deployed using the keyless deployment method— s: 0x0820820820820820820820820820820820820820820820820820820820820820 ``` -This nice `s` value---made of a repetion of `820`---is a predicatable "random number" generated deterministically by a human. + This `s` value---made of a repeating pattern of `820`'s---is a predictable "random number" generated deterministically by a human. -3. We recover the sender of this transaction, i.e. the deployment account. + > The value of `s` must be 64 bytes long. Since `820` is 3 bytes long and 3 is not a divisor of 64, but it is a divisor of 63, the `s` value is prefixed with a single zero (`0`). The `0` prefix also guarantees that `s < secp256k1n ÷ 2 + 1`. + +3. We recover the sender of this transaction, i.e., the single-use deployment account. > Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account. -4. Send Ether to this deployment account. +4. Send exactly 0.08 ethers to this single-use deployment account. -5. Broadcast the transaction. +5. Broadcast the deployment transaction. -This operation can be done on any chain, guaranteeing that the contract address is going to always be the same and nobody will be able to mess up that address with a different contract. +This operation can be done on any chain, guaranteeing that the contract address is always the same and nobody can use that address with a different contract. -### Special Registry Deployment Account +### Single-use Registry Deployment Account ``` 0xC3AdeE9B2E23837DF6259A984Af7a437dE4E2ab6 @@ -302,13 +304,16 @@ This operation can be done on any chain, guaranteeing that the contract address This account is generated by reverse engineering it from its signature for the transaction. This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction. -### Deployed contract +> To deploy the registry, 0.08 ethers MUST be sent to this account *first*. + +### Registry Contract Address ``` 0x820d0Bc4d0AD9E0E7dc19BD8cF9C566FC86054ce ``` -The contract has the address above for every chain it is deployed to. +The contract has the address above for every chain on which it is deployed. +
    Raw metadata of ./contracts/ERC820Registry.sol
    @@ -640,6 +645,19 @@ Any interface name is hashed using `keccak256` and sent to `getInterfaceImplemen
     
     If the interface is part of a standard, it is best practice to explicitly state the interface name and link to this published [ERC820] such that other people don't have to come here to look up these rules.
     
    +For convenience the registry provides a function to compute the hash on-chain:
    +
    +``` solidity
    +function interfaceHash(string _interfaceName) public pure returns(bytes32)
    +```
    +
    +Compute the keccak256 hash of an interface given its name.
    +
    +> **identifier:** `65ba36c1`  
    +> **parameters**  
    +> `_interfaceName`: Name of the interface.  
    +> **returns:** The `keccak256` hash of an interface name.
    +
     #### **Approved ERCs**
     
     If the interface is part of an approved ERC, it MUST be named `ERC###XXXXX` where `###` is the number of the ERC and XXXXX should be the name of the interface in CamelCase. The meaning of this interface SHOULD be defined in the specified ERC.
    @@ -655,6 +673,38 @@ Examples:
     
     Any interface where the last 28 bytes are zeroes (`0`) SHALL be considered an [ERC165] interface.
     
    +**[ERC165] Lookup**
    +
    +Anyone can explicitly check if a contract implements an [ERC165] interface using the registry by calling one of the two functions below:
    +
    +``` solidity
    +function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool)
    +```
    +
    +Checks whether a contract implements an [ERC165] interface or not.
    +
    +The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`.
    +
    +*NOTE*: This function may modify the state when updating the cache. However, this function must have the `view` modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the [ERC165] cache is updated.
    +
    +> **identifier:** `f712f3e8`  
    +> **parameters**  
    +> `_contract`: Address of the contract to check.  
    +> `_interfaceId`: [ERC165] interface to check.  
    +> **returns:** `true` if `_contract` implements `_interfaceId`, false otherwise.
    +
    +``` solidity
    +function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool)
    +```
    +
    +Checks whether a contract implements an [ERC165] interface or not without using nor updating the cache.
    +
    +> **identifier:** `b7056765`  
    +> **parameters**  
    +> `_contract`: Address of the contract to check.  
    +> `_interfaceId`: [ERC165] interface to check.  
    +> **returns:** `true` if `_contract` implements `_interfaceId`, false otherwise.
    +
     **[ERC165] Cache**
     
     Whether a contract implements an [ERC165] interface or not is automatically cached during [lookup] if the lookup is part of a transaction.
    @@ -665,9 +715,10 @@ If a contract dynamically changes its interface, that contract SHOULD update the
     function updateERC165Cache(address _contract, bytes4 _interfaceId) public
     ```
     
    +> **identifier:** `a41e7d51`  
     > **parameters**  
     > `_contract`: Address of the contract for which to update the cache.  
    -> `_interfaceHash`: ERC165 interface for which to update the cache.
    +> `_interfaceHash`: [ERC165] interface for which to update the cache.
     
     #### **Private User-defined Interfaces**
     
    @@ -681,7 +732,7 @@ For any address to set a contract as the interface implementation, it must call
     function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) public
     ```
     
    -Sets the contract that will handle a specific interface.
    +Sets the contract which implements a specific interface for an address.
     
     Only the `manager` defined for that address can set it. (Each address is the manager for itself, see the [manager] section for more details.)
     
    @@ -692,6 +743,7 @@ Only the `manager` defined for that address can set it. (Each address is the man
     
     *NOTE*: The `_interfaceHash` MUST NOT be an [ERC165] interface—it MUST NOT end with 28 zeroes (`0`).
     
    +> **identifier:** `29965a1d`  
     > **parameters**  
     > `_addr`: Address to define the interface for (if `_addr == 0` them `msg.sender`: is assumed)  
     > `_interfaceHash`: `keccak256` hash of the name of the interface as a string, for example `web3.utils.keccak256('ERC777TokensRecipient')` for the ERC777TokensRecipient interface.
    @@ -708,6 +760,7 @@ Query if an address implements an interface and through which contract.
     
     *NOTE*: If the last 28 bytes of the `_interfaceHash` are zeroes (`0`), then the first 4 bytes are considered an [ERC165] interface and the registry SHALL forward the call to the contract at `_addr` to see if it implements the [ERC165] interface (the first 4 bytes of `_interfaceHash`). The registry SHALL also cache [ERC165] queries to reduce gas consumption. Anyone MAY call the `erc165UpdateCache` function to update whether a contract implements an interface or not.
     
    +> **identifier:** `aabbb8ca`  
     > **parameters**  
     > `_addr`: Address being queried for the implementer of an interface. (If `_addr == 0` them `msg.sender` is assumed.)  
     > `_interfaceHash`: keccak256 hash of the name of the interface as a string. E.g. `web3.utils.keccak256('ERC777Token')`  
    @@ -736,9 +789,11 @@ Indicates whether a contract implements an interface (`interfaceHash`) for a giv
     
     If a contract implements the interface (`interfaceHash`) for a given address (`addr`), it MUST return `ERC820_ACCEPT_MAGIC` when called with the `addr` and the `interfaceHash`. If it does not implement the `interfaceHash` for a given address (`addr`), it MUST NOT return `ERC820_ACCEPT_MAGIC`.
     
    +> **identifier:** `f0083250`  
     > **parameters**  
     > `addr`: Address for which the interface is implemented  
    -> `interfaceHash`: Hash of the interface which is implemented
    +> `interfaceHash`: Hash of the interface which is implemented  
    +> **returns:** `ERC820_ACCEPT_MAGIC` only if the contract implements `ìnterfaceHash` for the address `addr`.
     
     The special value `ERC820_ACCEPT_MAGIC` is defined as the `keccka256` hash of the string `"ERC820_ACCEPT_MAGIC"`.
     
    @@ -752,7 +807,7 @@ bytes32 constant ERC820_ACCEPT_MAGIC = keccak256("ERC820_ACCEPT_MAGIC");
     
     The manager of an address (regular account or a contract) is the only entity allowed to register implementations of interfaces for the address. By default, any address is its own manager.
     
    -The manager can transfer its role to another address by calling `setManager` with the address for which to transfer the manager and the address of the new manager.
    +The manager can transfer its role to another address by calling `setManager` on the registry contract with the address for which to transfer the manager and the address of the new manager.
     
     **`setManager` function**
     
    @@ -767,6 +822,7 @@ The new manager will be able to call `setInterfaceImplementer` for `_addr`.
     If `_addr` is `0x0`, `msg.sender` is assumed for `_addr`.  
     If `_newManager` is `0x0`, the manager is reset to `_addr` itself as the manager.
     
    +> **identifier:** `5df8122f`  
     > **parameters**  
     > `_addr`: Address for which to set the new manager. (Pass `0x0` to use `msg.sender` as the address.)  
     > `_newManager`: The address of the new manager for `_addr`. (Pass `0x0` to reset the manager to `_addr`.)
    @@ -779,6 +835,7 @@ function getManager(address _addr) public view returns(address)
     
     Get the manager of an address.
     
    +> **identifier:** `3d584063`  
     > **parameters**  
     > `_addr`: Address for which to return the manager.  
     > **returns:** Address of the manager for a given address.
    @@ -809,3 +866,4 @@ Copyright and related rights waived via [CC0].
     [jbaylina/eip820]: https://github.com/jbaylina/eip820
     [CC0]: https://creativecommons.org/publicdomain/zero/1.0/
     [Nick]: https://github.com/Arachnid/
    +[ENS]: https://ens.domains/
    
    From b0762854591de9cccad475dbcab9948b65dbe094 Mon Sep 17 00:00:00 2001
    From: Ayrat Badykov 
    Date: Tue, 18 Sep 2018 12:19:22 +0300
    Subject: [PATCH 143/177] Automatically merged updates to draft EIP(s) 1283
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-1283.md | 18 +++++++++---------
     1 file changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md
    index 14c42626..7ee488fa 100644
    --- a/EIPS/eip-1283.md
    +++ b/EIPS/eip-1283.md
    @@ -13,15 +13,15 @@ created: 2018-08-01
     
     This EIP proposes net gas metering changes for SSTORE opcode, as an
     alternative for EIP-1087. It tries to be friendlier to implementations
    -that uses different opetimiazation strategies for storage change
    +that use different optimization strategies for storage change
     caches.
     
     ## Motivation
     
     EIP-1087 proposes a way to adjust gas metering for SSTORE opcode,
    -enabling new usages on this opcodes where it is previously too
    +enabling new usages on these opcodes where it is previously too
     expensive. However, EIP-1087 requires keeping a dirty map for storage
    -changes, and implictly makes the assumption that a transaction's
    +changes, and implicitly makes the assumption that a transaction's
     storage changes are committed to the storage trie at the end of a
     transaction. This works well for some implementations, but not for
     others. After EIP-658, an efficient storage cache implementation would
    @@ -29,11 +29,11 @@ probably use an in-memory trie (without RLP encoding/decoding) or
     other immutable data structures to keep track of storage changes, and
     only commit changes at the end of a block. For them, it is possible to
     know a storage's original value and current value, but it is not
    -possible to iterate over all storage changes without incur additional
    +possible to iterate over all storage changes without incurring additional
     memory or processing costs.
     
     This EIP proposes an alternative way for gas metering on SSTORE, using
    -information that is more universially available to most
    +information that is more universally available to most
     implementations:
     
     * *Storage slot's original value*.
    @@ -43,7 +43,7 @@ implementations:
     For the specification provided here:
     
     * We don't suffer from the optimization limitation of EIP-1087, and it
    -  never costs more gas compared with current scheme.
    +  never costs more gas compared with the current scheme.
     * It covers all usages for a transient storage. Clients that are easy
       to implement EIP-1087 will also be easy to implement this
       specification. Some other clients might require a little bit extra
    @@ -100,7 +100,7 @@ consumed.
     
     ## Explanation
     
    -The new gas cost scheme for SSTORE is divided to three different
    +The new gas cost scheme for SSTORE is divided into three different
     types:
     
     * **No-op**: the virtual machine does not need to do anything. This is
    @@ -143,7 +143,7 @@ state because that is trivial:
     
     ![State Transition](../assets/eip-1283/state.png)
     
    -Below are table version of the above diagram. Vertical shows the *new
    +Below is table version of the above diagram. Vertical shows the *new
     value* being set, and horizontal shows the state of *original value*
     and *current value*.
     
    @@ -187,7 +187,7 @@ Examine examples provided in EIP-1087's Motivation:
     ## Backwards Compatibility
     
     This EIP requires a hard fork to implement. No gas cost increase is
    -anticipated, and many contract will see gas reduction.
    +anticipated, and many contracts will see gas reduction.
     
     ## Test Cases
     
    
    From 14f978f568f2fa863c6bab585c03b2188cd67df9 Mon Sep 17 00:00:00 2001
    From: Jacques Dafflon 
    Date: Tue, 18 Sep 2018 23:50:55 +0200
    Subject: [PATCH 144/177] Automatically merged updates to draft EIP(s) 820
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-820.md | 28 +++++++++++++++-------------
     1 file changed, 15 insertions(+), 13 deletions(-)
    
    diff --git a/EIPS/eip-820.md b/EIPS/eip-820.md
    index 504ee09f..ae6a5b85 100644
    --- a/EIPS/eip-820.md
    +++ b/EIPS/eip-820.md
    @@ -17,7 +17,7 @@ This standard keeps backward compatibility with [ERC165].
     
     ## Abstract
     
    -This standard defines a registry where smart contracts and regular accounts can publish which functionalities they implement—either directly or through a proxy contract.
    +This standard defines a registry where smart contracts and regular accounts can publish which functionalities they implement---either directly or through a proxy contract.
     
     Anyone can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation.
     
    @@ -70,7 +70,7 @@ This standard also provides a *unique* address for all chains. Thus solving the
      */
     pragma solidity 0.4.24;
     // IV is value needed to have a vanity address starting with `0x820`.
    -// IV: 1200
    +// IV: 2241
     
     /// @dev The interface a contract MUST implement if it is the implementer of
     /// some (other) interface for any address other than itself.
    @@ -260,14 +260,14 @@ contract ERC820Registry {
     Below is the raw transaction which MUST be used to deploy the smart contract on any chain.
     
     ```
    -0xf90ab18085174876e800830c35008080b90a5e608060405234801561001057600080fd5b50610a3e806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf5780635df8122f146100fc57806365ba36c114610123578063a41e7d511461018e578063aabbb8ca146101bc578063b7056765146101e0578063f712f3e814610222575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610250565b005b3480156100cb57600080fd5b506100e0600160a060020a036004351661054b565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a0360043581169060243516610597565b34801561012f57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261017c9436949293602493928401919081908401838280828437509497506106aa9650505050505050565b60408051918252519081900360200190f35b34801561019a57600080fd5b506100bd600160a060020a0360043516600160e060020a031960243516610774565b3480156101c857600080fd5b506100e0600160a060020a03600435166024356107fe565b3480156101ec57600080fd5b5061020e600160a060020a0360043516600160e060020a031960243516610878565b604080519115158252519081900360200190f35b34801561022e57600080fd5b5061020e600160a060020a0360043516600160e060020a03196024351661092d565b6000600160a060020a038416156102675783610269565b335b9050336102758261054b565b600160a060020a0316146102d3576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6102dc836109a3565b15610331576040805160e560020a62461bcd02815260206004820152601960248201527f4d757374206e6f74206265206120455243313635206861736800000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103525750600160a060020a0382163314155b156104da5760405160200180807f4552433832305f4143434550545f4d414749430000000000000000000000000081525060130190506040516020818303038152906040526040518082805190602001908083835b602083106103c65780518252601f1990920191602091820191016103a7565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207ff0083250000000000000000000000000000000000000000000000000000000008352600160a060020a038881166004850152602484018b90529451909650938816945063f0083250936044808401945091929091908290030181600087803b15801561045957600080fd5b505af115801561046d573d6000803e3d6000fd5b505050506040513d602081101561048357600080fd5b5051146104da576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a038082166000908152600160205260408120549091161515610575575080610592565b50600160a060020a03808216600090815260016020526040902054165b919050565b6000600160a060020a038316156105ae57826105b0565b335b9050336105bc8261054b565b600160a060020a03161461061a576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b80600160a060020a031682600160a060020a031614610639578161063c565b60005b600160a060020a03828116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519185169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a3505050565b6000816040516020018082805190602001908083835b602083106106df5780518252601f1990920191602091820191016106c0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106107425780518252601f199092019160209182019101610723565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b61077e8282610878565b61078957600061078b565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b60008080600160a060020a038516156108175784610819565b335b9150610824846109a3565b15610849575082610835828261092d565b610840576000610842565b815b9250610870565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b600080806108a6857f01ffc9a7000000000000000000000000000000000000000000000000000000006109c5565b90925090508115806108b6575080155b156108c45760009250610870565b6108d685600160e060020a03196109c5565b90925090508115806108e757508015155b156108f55760009250610870565b6108ff85856109c5565b90925090506001821480156109145750806001145b156109225760019250610870565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff16151561096e5761096e8383610774565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a72305820aebcc5581490dc97cade67a16777bfda36414f87fac1422cc9f37c2d60691cea00291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00820820820820820820820820820820820820820820820820820820820820820
    +0xf90ab18085174876e800830c35008080b90a5e608060405234801561001057600080fd5b50610a3e806100206000396000f30060806040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bf5780635df8122f146100fc57806365ba36c114610123578063a41e7d511461018e578063aabbb8ca146101bc578063b7056765146101e0578063f712f3e814610222575b600080fd5b34801561009e57600080fd5b506100bd600160a060020a036004358116906024359060443516610250565b005b3480156100cb57600080fd5b506100e0600160a060020a036004351661054b565b60408051600160a060020a039092168252519081900360200190f35b34801561010857600080fd5b506100bd600160a060020a0360043581169060243516610597565b34801561012f57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261017c9436949293602493928401919081908401838280828437509497506106aa9650505050505050565b60408051918252519081900360200190f35b34801561019a57600080fd5b506100bd600160a060020a0360043516600160e060020a031960243516610774565b3480156101c857600080fd5b506100e0600160a060020a03600435166024356107fe565b3480156101ec57600080fd5b5061020e600160a060020a0360043516600160e060020a031960243516610878565b604080519115158252519081900360200190f35b34801561022e57600080fd5b5061020e600160a060020a0360043516600160e060020a03196024351661092d565b6000600160a060020a038416156102675783610269565b335b9050336102758261054b565b600160a060020a0316146102d3576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6102dc836109a3565b15610331576040805160e560020a62461bcd02815260206004820152601960248201527f4d757374206e6f74206265206120455243313635206861736800000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103525750600160a060020a0382163314155b156104da5760405160200180807f4552433832305f4143434550545f4d414749430000000000000000000000000081525060130190506040516020818303038152906040526040518082805190602001908083835b602083106103c65780518252601f1990920191602091820191016103a7565b51815160209384036101000a6000190180199092169116179052604080519290940182900382207ff0083250000000000000000000000000000000000000000000000000000000008352600160a060020a038881166004850152602484018b90529451909650938816945063f0083250936044808401945091929091908290030181600087803b15801561045957600080fd5b505af115801561046d573d6000803e3d6000fd5b505050506040513d602081101561048357600080fd5b5051146104da576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a038082166000908152600160205260408120549091161515610575575080610592565b50600160a060020a03808216600090815260016020526040902054165b919050565b6000600160a060020a038316156105ae57826105b0565b335b9050336105bc8261054b565b600160a060020a03161461061a576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b80600160a060020a031682600160a060020a031614610639578161063c565b60005b600160a060020a03828116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519185169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a3505050565b6000816040516020018082805190602001908083835b602083106106df5780518252601f1990920191602091820191016106c0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106107425780518252601f199092019160209182019101610723565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912095945050505050565b61077e8282610878565b61078957600061078b565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b60008080600160a060020a038516156108175784610819565b335b9150610824846109a3565b15610849575082610835828261092d565b610840576000610842565b815b9250610870565b600160a060020a038083166000908152602081815260408083208884529091529020541692505b505092915050565b600080806108a6857f01ffc9a7000000000000000000000000000000000000000000000000000000006109c5565b90925090508115806108b6575080155b156108c45760009250610870565b6108d685600160e060020a03196109c5565b90925090508115806108e757508015155b156108f55760009250610870565b6108ff85856109c5565b90925090506001821480156109145750806001145b156109225760019250610870565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff16151561096e5761096e8383610774565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160088189617530fa9051909690955093505050505600a165627a7a72305820e3db118fca3ca7b01f94f0360177adb045fd6d1e613065f54a0a71f84318cdc300291ba08208208208208208208208208208208208208208208208208208208208208200a00820820820820820820820820820820820820820820820820820820820820820
     ```
     
    -The string of `820`'s at the end of the transaction is the `s` of the signature. From this pattern, one can clearly deduce that it is a deterministic signature generated by a human.
    +The strings of `820`'s at the end of the transaction are the `r` and `s` of the signature. From this pattern, one can clearly deduce that it is a deterministic signature generated by a human.
     
     ### Deployment Method
     
    -This contract is going to be deployed using the keyless deployment method—also known as [Nick]'s method—which relies on a single-use address. (See [Nick's article] for more details). This method works as follows:
    +This contract is going to be deployed using the keyless deployment method---also known as [Nick]'s method---which relies on a single-use address. (See [Nick's article] for more details). This method works as follows:
     
     1. Generate a transaction which deploys the contract from a new random account.
       - This transaction MUST NOT use [EIP155] in order to work on any chain.
    @@ -277,13 +277,15 @@ This contract is going to be deployed using the keyless deployment method—
     
        ```
        v: 27
    -   r: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
    +   r: 0x8208208208208208208208208208208208208208208208208208208208208200
        s: 0x0820820820820820820820820820820820820820820820820820820820820820
        ```
     
    -   This `s` value---made of a repeating pattern of `820`'s---is a predictable "random number" generated deterministically by a human.
    +   Those `r` and `s` values---made of a repeating pattern of `820`'s---are predictable "random numbers" generated deterministically by a human.
     
    -   > The value of `s` must be 64 bytes long. Since `820` is 3 bytes long and 3 is not a divisor of 64, but it is a divisor of 63, the `s` value is prefixed with a single zero (`0`). The `0` prefix also guarantees that `s < secp256k1n ÷ 2 + 1`.
    +   > The values of `r` and `s` must be 32 bytes long each---or 64 characters in hexadecimal. Since `820` is 3 characters long and 3 is not a divisor of 64, but it is a divisor of 63, the `r` and `s` values are padded with one extra character.  
    +   > The `s` value is prefixed with a single zero (`0`). The `0` prefix also guarantees that `s < secp256k1n ÷ 2 + 1`.  
    +   > The `r` value, cannot be prefixed with a zero, as the transaction becomes invalid. Instead it is suffixed with a zero (`0`) which still respects the condition `s < secp256k1n`.
     
     3. We recover the sender of this transaction, i.e., the single-use deployment account.
     
    @@ -299,7 +301,7 @@ This operation can be done on any chain, guaranteeing that the contract address
     ### Single-use Registry Deployment Account
     
     ```
    -0xC3AdeE9B2E23837DF6259A984Af7a437dE4E2ab6
    +0x2681AFA843b492f3d7851afCeca7385a3D13fCE0
     ```
     
     This account is generated by reverse engineering it from its signature for the transaction. This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction.
    @@ -309,7 +311,7 @@ This account is generated by reverse engineering it from its signature for the t
     ### Registry Contract Address
     
     ```
    -0x820d0Bc4d0AD9E0E7dc19BD8cF9C566FC86054ce
    +0x820c4597Fc3E4193282576750Ea4fcfe34DdF0a7
     ```
     
     The contract has the address above for every chain on which it is deployed.
    @@ -630,8 +632,8 @@ The contract has the address above for every chain on which it is deployed.
       },
       "sources": {
         "./contracts/ERC820Registry.sol": {
    -      "content": "/* ERC820: Pseudo-introspection Registry Contract\n * by Jordi Baylina and Jacques Dafflon\n *\n * To the extent possible under law, Jordi Baylina and Jacques Dafflon who\n * associated CC0 with the ERC820: Pseudo-introspection Registry Contract have\n * waived all copyright and related or neighboring rights to the\n * ERC820: Pseudo-introspection Registry Contract.\n *\n * You should have received a copy of the CC0 legalcode along with this work.\n * If not, see .\n *\n *    ███████╗██████╗  ██████╗ █████╗ ██████╗  ██████╗\n *    ██╔════╝██╔══██╗██╔════╝██╔══██╗╚════██╗██╔═████╗\n *    █████╗  ██████╔╝██║     ╚█████╔╝ █████╔╝██║██╔██║\n *    ██╔══╝  ██╔══██╗██║     ██╔══██╗██╔═══╝ ████╔╝██║\n *    ███████╗██║  ██║╚██████╗╚█████╔╝███████╗╚██████╔╝\n *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝\n *\n *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗\n *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝\n *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝\n *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝\n *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║\n *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝\n *\n */\npragma solidity 0.4.24;\n// IV is value needed to have a vanity address starting with `0x820`.\n// IV: 1200\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some (other) interface for any address other than itself.\ninterface ERC820ImplementerInterface {\n    /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.\n    /// @param addr Address for which the contract will implement the interface\n    /// @param interfaceHash keccak256 hash of the name of the interface\n    /// @return ERC820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`.\n    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32);\n}\n\n\n/// @title ERC820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820\ncontract ERC820Registry {\n    /// @notice ERC165 Invalid ID.\n    bytes4 constant INVALID_ID = 0xffffffff;\n    /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).\n    bytes4 constant ERC165ID = 0x01ffc9a7;\n    /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.\n    bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked(\"ERC820_ACCEPT_MAGIC\"));\n\n    mapping (address => mapping(bytes32 => address)) interfaces;\n    mapping (address => address) managers;\n    mapping (address => mapping(bytes4 => bool)) erc165Cached;\n\n    /// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.\n    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n    /// @notice Indicates `newManager` is the address of the new manager for `addr`.\n    event ManagerChanged(address indexed addr, address indexed newManager);\n\n    /// @notice Query if an address implements an interface and through which contract.\n    /// @param _addr Address being queried for the implementer of an interface.\n    /// (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// E.g., `web3.utils.keccak256('ERC777Token')`.\n    /// @return The address of the contract which implements the interface `_interfaceHash` for `_addr`\n    /// or `0x0` if `_addr` did not register an implementer for this interface.\n    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        if (isERC165Interface(_interfaceHash)) {\n            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0;\n        }\n        return interfaces[addr][_interfaceHash];\n    }\n\n    /// @notice Sets the contract which implements a specific interface for an address.\n    /// Only the manager defined for that address can set it.\n    /// (Each address is the manager for itself until it sets a new manager.)\n    /// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface.\n    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n\n        require(!isERC165Interface(_interfaceHash), \"Must not be a ERC165 hash\");\n        if (_implementer != 0 && _implementer != msg.sender) {\n            require(\n                ERC820ImplementerInterface(_implementer)\n                    .canImplementInterfaceForAddress(addr, _interfaceHash) == ERC820_ACCEPT_MAGIC,\n                \"Does not implement the interface\"\n            );\n        }\n        interfaces[addr][_interfaceHash] = _implementer;\n        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);\n    }\n\n    /// @notice Sets the `_newManager` as manager for the `_addr` address.\n    /// The new manager will be able to call `setInterfaceImplementer` for `_addr`.\n    /// @param _addr Address for which to set the new manager. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _newManager Address of the new manager for `addr`. (Pass `0x0` to reset the manager to `_addr` itself.)\n    function setManager(address _addr, address _newManager) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n        managers[addr] = _newManager == addr ? 0 : _newManager;\n        emit ManagerChanged(addr, _newManager);\n    }\n\n    /// @notice Get the manager of an address.\n    /// @param _addr Address for which to return the manager.\n    /// @return Address of the manager for a given address.\n    function getManager(address _addr) public view returns(address) {\n        // By default the manager of an address is the same address\n        if (managers[_addr] == 0) {\n            return _addr;\n        } else {\n            return managers[_addr];\n        }\n    }\n\n    /// @notice Compute the keccak256 hash of an interface given its name.\n    /// @param _interfaceName Name of the interface.\n    /// @return The keccak256 hash of an interface name.\n    function interfaceHash(string _interfaceName) public pure returns(bytes32) {\n        return keccak256(abi.encodePacked(_interfaceName));\n    }\n\n    /* --- ERC165 Related Functions --- */\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not.\n    /// The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    /// @dev This function may modify the state when updating the cache. However, this function must have the `view`\n    /// modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the ERC165 cache\n    /// is updated.\n    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        if (!erc165Cached[_contract][_interfaceId]) {\n            updateERC165Cache(_contract, _interfaceId);\n        }\n        return interfaces[_contract][_interfaceId] != 0;\n    }\n\n    /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n    /// @param _contract Address of the contract for which to update the cache.\n    /// @param _interfaceId ERC165 interface for which to update the cache.\n    function updateERC165Cache(address _contract, bytes4 _interfaceId) public {\n        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0;\n        erc165Cached[_contract][_interfaceId] = true;\n    }\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        uint256 success;\n        uint256 result;\n\n        (success, result) = noThrowCall(_contract, ERC165ID);\n        if (success == 0 || result == 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, INVALID_ID);\n        if (success == 0 || result != 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, _interfaceId);\n        if (success == 1 && result == 1) {\n            return true;\n        }\n        return false;\n    }\n\n    /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n    /// @param _interfaceHash The hash to check.\n    /// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise.\n    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n    }\n\n    function noThrowCall(address _contract, bytes4 _interfaceId)\n        internal view returns (uint256 success, uint256 result)\n    {\n        bytes4 erc165ID = ERC165ID;\n\n        assembly {\n                let x := mload(0x40)               // Find empty storage location using \"free memory pointer\"\n                mstore(x, erc165ID)                // Place signature at begining of empty storage\n                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n                success := staticcall(\n                    30000,                         // 30k gas\n                    _contract,                     // To addr\n                    x,                             // Inputs are stored at location x\n                    0x08,                          // Inputs are 8 bytes long\n                    x,                             // Store output over input (saves space)\n                    0x20                           // Outputs are 32 bytes long\n                )\n\n                result := mload(x)                 // Load the result\n        }\n    }\n}\n",
    -      "keccak256": "0xbc18b6a4ad0aeb405e2ebae976b0ab653c17b3ba60eeaf0f1cf740a26bdcb7f1"
    +      "content": "/* ERC820: Pseudo-introspection Registry Contract\n * by Jordi Baylina and Jacques Dafflon\n *\n * To the extent possible under law, Jordi Baylina and Jacques Dafflon who\n * associated CC0 with the ERC820: Pseudo-introspection Registry Contract have\n * waived all copyright and related or neighboring rights to the\n * ERC820: Pseudo-introspection Registry Contract.\n *\n * You should have received a copy of the CC0 legalcode along with this work.\n * If not, see .\n *\n *    ███████╗██████╗  ██████╗ █████╗ ██████╗  ██████╗\n *    ██╔════╝██╔══██╗██╔════╝██╔══██╗╚════██╗██╔═████╗\n *    █████╗  ██████╔╝██║     ╚█████╔╝ █████╔╝██║██╔██║\n *    ██╔══╝  ██╔══██╗██║     ██╔══██╗██╔═══╝ ████╔╝██║\n *    ███████╗██║  ██║╚██████╗╚█████╔╝███████╗╚██████╔╝\n *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚════╝ ╚══════╝ ╚═════╝\n *\n *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗\n *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝\n *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝\n *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝\n *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║\n *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝\n *\n */\npragma solidity 0.4.24;\n// IV is value needed to have a vanity address starting with `0x820`.\n// IV: 2241\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some (other) interface for any address other than itself.\ninterface ERC820ImplementerInterface {\n    /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not.\n    /// @param addr Address for which the contract will implement the interface\n    /// @param interfaceHash keccak256 hash of the name of the interface\n    /// @return ERC820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`.\n    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) public view returns(bytes32);\n}\n\n\n/// @title ERC820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-820\ncontract ERC820Registry {\n    /// @notice ERC165 Invalid ID.\n    bytes4 constant INVALID_ID = 0xffffffff;\n    /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).\n    bytes4 constant ERC165ID = 0x01ffc9a7;\n    /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.\n    bytes32 constant ERC820_ACCEPT_MAGIC = keccak256(abi.encodePacked(\"ERC820_ACCEPT_MAGIC\"));\n\n    mapping (address => mapping(bytes32 => address)) interfaces;\n    mapping (address => address) managers;\n    mapping (address => mapping(bytes4 => bool)) erc165Cached;\n\n    /// @notice Indicates a contract is the `implementer` of `interfaceHash` for `addr`.\n    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n    /// @notice Indicates `newManager` is the address of the new manager for `addr`.\n    event ManagerChanged(address indexed addr, address indexed newManager);\n\n    /// @notice Query if an address implements an interface and through which contract.\n    /// @param _addr Address being queried for the implementer of an interface.\n    /// (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// E.g., `web3.utils.keccak256('ERC777Token')`.\n    /// @return The address of the contract which implements the interface `_interfaceHash` for `_addr`\n    /// or `0x0` if `_addr` did not register an implementer for this interface.\n    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        if (isERC165Interface(_interfaceHash)) {\n            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : 0;\n        }\n        return interfaces[addr][_interfaceHash];\n    }\n\n    /// @notice Sets the contract which implements a specific interface for an address.\n    /// Only the manager defined for that address can set it.\n    /// (Each address is the manager for itself until it sets a new manager.)\n    /// @param _addr Address to define the interface for. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _interfaceHash keccak256 hash of the name of the interface as a string.\n    /// For example, `web3.utils.keccak256('ERC777TokensRecipient')` for the `ERC777TokensRecipient` interface.\n    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n\n        require(!isERC165Interface(_interfaceHash), \"Must not be a ERC165 hash\");\n        if (_implementer != 0 && _implementer != msg.sender) {\n            require(\n                ERC820ImplementerInterface(_implementer)\n                    .canImplementInterfaceForAddress(addr, _interfaceHash) == ERC820_ACCEPT_MAGIC,\n                \"Does not implement the interface\"\n            );\n        }\n        interfaces[addr][_interfaceHash] = _implementer;\n        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);\n    }\n\n    /// @notice Sets the `_newManager` as manager for the `_addr` address.\n    /// The new manager will be able to call `setInterfaceImplementer` for `_addr`.\n    /// @param _addr Address for which to set the new manager. (If `_addr == 0` then `msg.sender` is assumed.)\n    /// @param _newManager Address of the new manager for `addr`. (Pass `0x0` to reset the manager to `_addr` itself.)\n    function setManager(address _addr, address _newManager) external {\n        address addr = _addr == 0 ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n        managers[addr] = _newManager == addr ? 0 : _newManager;\n        emit ManagerChanged(addr, _newManager);\n    }\n\n    /// @notice Get the manager of an address.\n    /// @param _addr Address for which to return the manager.\n    /// @return Address of the manager for a given address.\n    function getManager(address _addr) public view returns(address) {\n        // By default the manager of an address is the same address\n        if (managers[_addr] == 0) {\n            return _addr;\n        } else {\n            return managers[_addr];\n        }\n    }\n\n    /// @notice Compute the keccak256 hash of an interface given its name.\n    /// @param _interfaceName Name of the interface.\n    /// @return The keccak256 hash of an interface name.\n    function interfaceHash(string _interfaceName) public pure returns(bytes32) {\n        return keccak256(abi.encodePacked(_interfaceName));\n    }\n\n    /* --- ERC165 Related Functions --- */\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not.\n    /// The result is cached. If the cache is out of date, it must be updated by calling `updateERC165Cache`.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    /// @dev This function may modify the state when updating the cache. However, this function must have the `view`\n    /// modifier since `getInterfaceImplementer` also calls it. If called from within a transaction, the ERC165 cache\n    /// is updated.\n    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        if (!erc165Cached[_contract][_interfaceId]) {\n            updateERC165Cache(_contract, _interfaceId);\n        }\n        return interfaces[_contract][_interfaceId] != 0;\n    }\n\n    /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n    /// @param _contract Address of the contract for which to update the cache.\n    /// @param _interfaceId ERC165 interface for which to update the cache.\n    function updateERC165Cache(address _contract, bytes4 _interfaceId) public {\n        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(_contract, _interfaceId) ? _contract : 0;\n        erc165Cached[_contract][_interfaceId] = true;\n    }\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return `true` if `_contract` implements `_interfaceId`, false otherwise.\n    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        uint256 success;\n        uint256 result;\n\n        (success, result) = noThrowCall(_contract, ERC165ID);\n        if (success == 0 || result == 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, INVALID_ID);\n        if (success == 0 || result != 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, _interfaceId);\n        if (success == 1 && result == 1) {\n            return true;\n        }\n        return false;\n    }\n\n    /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n    /// @param _interfaceHash The hash to check.\n    /// @return `true` if the hash is a ERC165 interface (ending with 28 zeroes), `false` otherwise.\n    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n    }\n\n    function noThrowCall(address _contract, bytes4 _interfaceId)\n        internal view returns (uint256 success, uint256 result)\n    {\n        bytes4 erc165ID = ERC165ID;\n\n        assembly {\n                let x := mload(0x40)               // Find empty storage location using \"free memory pointer\"\n                mstore(x, erc165ID)                // Place signature at begining of empty storage\n                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n                success := staticcall(\n                    30000,                         // 30k gas\n                    _contract,                     // To addr\n                    x,                             // Inputs are stored at location x\n                    0x08,                          // Inputs are 8 bytes long\n                    x,                             // Store output over input (saves space)\n                    0x20                           // Outputs are 32 bytes long\n                )\n\n                result := mload(x)                 // Load the result\n        }\n    }\n}\n",
    +      "keccak256": "0x62a6edf7c4b0584f5fdffb4978cb084768572be3bdf9386577da889f001fb689"
         }
       },
       "version": 1
    @@ -741,7 +743,7 @@ Only the `manager` defined for that address can set it. (Each address is the man
     - The `_implementer` MUST implement the `ERC820ImplementerInterface` (detailed below).
     - Calling `canImplementInterfaceForAddress` on `_implementer` with the given `_addr` and  `_interfaceHash` MUST return the `ERC820_ACCEPT_MAGIC` value.
     
    -*NOTE*: The `_interfaceHash` MUST NOT be an [ERC165] interface—it MUST NOT end with 28 zeroes (`0`).
    +*NOTE*: The `_interfaceHash` MUST NOT be an [ERC165] interface---it MUST NOT end with 28 zeroes (`0`).
     
     > **identifier:** `29965a1d`  
     > **parameters**  
    
    From 1f6a3fdb90326f55cad688edb395344fa6ff1725 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Fri, 31 Aug 2018 13:36:56 +0100
    Subject: [PATCH 145/177] Reserve ewasm testnet chaind (66) in EIP155
    
    ---
     EIPS/eip-155.md | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/EIPS/eip-155.md b/EIPS/eip-155.md
    index 7716b6ab..a6739381 100644
    --- a/EIPS/eip-155.md
    +++ b/EIPS/eip-155.md
    @@ -62,4 +62,5 @@ This would provide a way to send transactions that work on Ethereum without work
     | 42             | Kovan                                      |
     | 61             | Ethereum Classic mainnet                   |
     | 62             | Ethereum Classic testnet                   |
    +| 66             | ewasm testnet                              |
     | 1337           | Geth private chains (default)              |
    
    From b6c339440a207809d9937e3c248b1a068511c5e6 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 21:08:38 +0100
    Subject: [PATCH 146/177] Fix backtick typos in EIP1
    
    ---
     EIPS/eip-1.md | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md
    index 01717cc6..e198fee1 100644
    --- a/EIPS/eip-1.md
    +++ b/EIPS/eip-1.md
    @@ -106,9 +106,9 @@ Each EIP must begin with an RFC 822 style header preamble, preceded and followed
     
     ` status:` 
     
    -`* review-period-end: YYYY-MM-DD
    +`* review-period-end:` YYYY-MM-DD
     
    -` type: `
    +` type:` 
     
     ` * category:` 
     
    
    From 7497eb1d71e5c9c546aecd127839ead50745735b Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 21:10:42 +0100
    Subject: [PATCH 147/177] Add emphasis to "exceptional statuses" the same way
     as in the other cases (above)
    
    ---
     EIPS/eip-1.md | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md
    index e198fee1..6de41908 100644
    --- a/EIPS/eip-1.md
    +++ b/EIPS/eip-1.md
    @@ -65,10 +65,10 @@ Each status change is requested by the EIP author and reviewed by the EIP editor
     
     Other exceptional statuses include:
     
    -* Deferred -- This is for core EIPs that have been put off for a future hard fork.
    -* Rejected -- An EIP that is fundamentally broken or a Core EIP that was rejected by the Core Devs and will not be implemented.
    -* Active -- This is similar to Final, but denotes an EIP which which may be updated without changing its EIP number.
    -* Superseded -- An EIP which was previously final but is no longer considered state-of-the-art. Another EIP will be in Final status and reference the Superseded EIP.
    +* **Deferred** -- This is for core EIPs that have been put off for a future hard fork.
    +* **Rejected** -- An EIP that is fundamentally broken or a Core EIP that was rejected by the Core Devs and will not be implemented.
    +* **Active** -- This is similar to Final, but denotes an EIP which which may be updated without changing its EIP number.
    +* **Superseded** -- An EIP which was previously final but is no longer considered state-of-the-art. Another EIP will be in Final status and reference the Superseded EIP.
     
     ## What belongs in a successful EIP?
     
    
    From fef63dc647a480f63ff2d43c8764f9b8ff836126 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 20:49:59 +0100
    Subject: [PATCH 148/177] Mark EIP5 as Superseded
    
    ---
     EIPS/eip-5.md | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/EIPS/eip-5.md b/EIPS/eip-5.md
    index ae6ad165..dfde87b3 100644
    --- a/EIPS/eip-5.md
    +++ b/EIPS/eip-5.md
    @@ -2,10 +2,11 @@
     eip: 5
     title: Gas Usage for `RETURN` and `CALL*`
     author: Christian Reitwiessner 
    -status: Draft
    +status: Superseded
     type: Standards Track
     category: Core
     created: 2015-11-22
    +superseded-by: 211
     ---
     
     ### Abstract
    
    From 359d49bd4c8d8e31941dc2f84e78b208c4cde062 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Thu, 23 Mar 2017 14:35:49 +0000
    Subject: [PATCH 149/177] Add meta EIP for hard forks
    
    ---
     EIPS/eip-draft_hardforks.md | 39 +++++++++++++++++++++++++++++++++++++
     1 file changed, 39 insertions(+)
     create mode 100644 EIPS/eip-draft_hardforks.md
    
    diff --git a/EIPS/eip-draft_hardforks.md b/EIPS/eip-draft_hardforks.md
    new file mode 100644
    index 00000000..c92dbf3b
    --- /dev/null
    +++ b/EIPS/eip-draft_hardforks.md
    @@ -0,0 +1,39 @@
    +## Preamble
    +
    +    EIP: 
    +    Title: Formal process of hard forks
    +    Author: Alex Beregszaszi
    +    Type: Meta
    +    Status: Draft
    +    Created: 2017-03-23
    +
    +## Simple Summary
    +
    +To describe the formal process of preparing and activating hard forks.
    +
    +## Abstract
    +
    +To provide a process of coordination on hard forks.
    +
    +## Motivation
    +
    +Today discussions about hard forks happen at various forums and sometimes in ad-hoc ways.
    +
    +## Specification
    +
    +A Meta EIP should be created as a draft as a pull request as soon as a hard fork is planned. This EIP should contain:
    +- the desired codename of the hard fork,
    +- list of all the EIPs included in the hard fork and
    +- activation block number once decided.
    +
    +The draft pull request shall be updated with summaries of the decisions around the hard fork. It should move in to `Accepted` state once the changes are frozen and in to the `Final` state once the hard fork has been activated.
    +
    +The Yellow Paper should be kept updated with the hard fork changes. It could either refer to the fork codename or the EIP number of the fork.
    +
    +## Rationale
    +
    +A meta EIP for coordinating the hard fork should help in visibility and traceability of the scope of changes as well as provide a simple name and/or number for referring to the proposed fork.
    +
    +## Copyright
    +
    +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
    
    From d37911f46373ef95bb185f464fbc88f3e53b2b17 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Mon, 27 Mar 2017 20:57:43 +0100
    Subject: [PATCH 150/177] Mention the require header
    
    ---
     EIPS/eip-draft_hardforks.md | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/EIPS/eip-draft_hardforks.md b/EIPS/eip-draft_hardforks.md
    index c92dbf3b..de83d782 100644
    --- a/EIPS/eip-draft_hardforks.md
    +++ b/EIPS/eip-draft_hardforks.md
    @@ -24,9 +24,10 @@ Today discussions about hard forks happen at various forums and sometimes in ad-
     A Meta EIP should be created as a draft as a pull request as soon as a hard fork is planned. This EIP should contain:
     - the desired codename of the hard fork,
     - list of all the EIPs included in the hard fork and
    -- activation block number once decided.
    +- activation block number once decided and
    +- the **Requires** header should point to the previous hard fork meta EIP.
     
    -The draft pull request shall be updated with summaries of the decisions around the hard fork. It should move in to `Accepted` state once the changes are frozen and in to the `Final` state once the hard fork has been activated.
    +The draft pull request shall be updated with summaries of the decisions around the hard fork. It should move in to the `Accepted` state once the changes are frozen and in to the `Final` state once the hard fork has been activated.
     
     The Yellow Paper should be kept updated with the hard fork changes. It could either refer to the fork codename or the EIP number of the fork.
     
    
    From f1f8371357d4c3e23682821bd585abb6c332c94a Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Sun, 23 Apr 2017 17:20:56 +0100
    Subject: [PATCH 151/177] Rename to EIP 233
    
    ---
     EIPS/{eip-draft_hardforks.md => eip-233.md} | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
     rename EIPS/{eip-draft_hardforks.md => eip-233.md} (98%)
    
    diff --git a/EIPS/eip-draft_hardforks.md b/EIPS/eip-233.md
    similarity index 98%
    rename from EIPS/eip-draft_hardforks.md
    rename to EIPS/eip-233.md
    index de83d782..4d8a93ba 100644
    --- a/EIPS/eip-draft_hardforks.md
    +++ b/EIPS/eip-233.md
    @@ -1,6 +1,6 @@
     ## Preamble
     
    -    EIP: 
    +    EIP: 233
         Title: Formal process of hard forks
         Author: Alex Beregszaszi
         Type: Meta
    
    From bcf9d603ac3c265641dc0b3594e0582c8102f2d0 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Sun, 23 Apr 2017 17:41:48 +0100
    Subject: [PATCH 152/177] Clarify the Accepted state
    
    ---
     EIPS/eip-233.md | 8 ++------
     1 file changed, 2 insertions(+), 6 deletions(-)
    
    diff --git a/EIPS/eip-233.md b/EIPS/eip-233.md
    index 4d8a93ba..6e15563f 100644
    --- a/EIPS/eip-233.md
    +++ b/EIPS/eip-233.md
    @@ -7,13 +7,9 @@
         Status: Draft
         Created: 2017-03-23
     
    -## Simple Summary
    -
    -To describe the formal process of preparing and activating hard forks.
    -
     ## Abstract
     
    -To provide a process of coordination on hard forks.
    +To describe the formal process of preparing and activating hard forks.
     
     ## Motivation
     
    @@ -27,7 +23,7 @@ A Meta EIP should be created as a draft as a pull request as soon as a hard fork
     - activation block number once decided and
     - the **Requires** header should point to the previous hard fork meta EIP.
     
    -The draft pull request shall be updated with summaries of the decisions around the hard fork. It should move in to the `Accepted` state once the changes are frozen and in to the `Final` state once the hard fork has been activated.
    +The draft pull request shall be updated with summaries of the decisions around the hard fork. It should move in to the `Accepted` state once the changes are frozen (i.e. all referenced EIPs are in the `Accepted` state) and in to the `Final` state once the hard fork has been activated.
     
     The Yellow Paper should be kept updated with the hard fork changes. It could either refer to the fork codename or the EIP number of the fork.
     
    
    From e66b36ef36f19a3a1dea28deb39ad0405f0d3051 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Sun, 23 Apr 2017 17:42:28 +0100
    Subject: [PATCH 153/177] Require the Yellow Paper to be up-to-date
    
    ---
     EIPS/eip-233.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/EIPS/eip-233.md b/EIPS/eip-233.md
    index 6e15563f..348f0815 100644
    --- a/EIPS/eip-233.md
    +++ b/EIPS/eip-233.md
    @@ -25,7 +25,7 @@ A Meta EIP should be created as a draft as a pull request as soon as a hard fork
     
     The draft pull request shall be updated with summaries of the decisions around the hard fork. It should move in to the `Accepted` state once the changes are frozen (i.e. all referenced EIPs are in the `Accepted` state) and in to the `Final` state once the hard fork has been activated.
     
    -The Yellow Paper should be kept updated with the hard fork changes. It could either refer to the fork codename or the EIP number of the fork.
    +The Yellow Paper must be kept updated with the hard fork changes. It could either refer to the fork codename or the EIP number of the fork. The hard fork cannot be activated before the Yellow Paper is updated.
     
     ## Rationale
     
    
    From 2acea6ec7f814b68e09ecc4a2df971f4559f46e1 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 20:19:59 +0100
    Subject: [PATCH 154/177] Update to new EIP header
    
    ---
     EIPS/eip-233.md | 16 ++++++++--------
     1 file changed, 8 insertions(+), 8 deletions(-)
    
    diff --git a/EIPS/eip-233.md b/EIPS/eip-233.md
    index 348f0815..daf86cbf 100644
    --- a/EIPS/eip-233.md
    +++ b/EIPS/eip-233.md
    @@ -1,11 +1,11 @@
    -## Preamble
    -
    -    EIP: 233
    -    Title: Formal process of hard forks
    -    Author: Alex Beregszaszi
    -    Type: Meta
    -    Status: Draft
    -    Created: 2017-03-23
    +---
    +eip: 233
    +title: Formal process of hard forks
    +author: Alex Beregszaszi (@axic)
    +type: Meta
    +status: Draft
    +created: 2017-03-23
    +---
     
     ## Abstract
     
    
    From 35b75c130da858ea429e60f6af52c1eeff15a081 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 20:22:13 +0100
    Subject: [PATCH 155/177] Update wording to reflect the new EIP process (drafts
     are merged and pull requests are not kept open)
    
    ---
     EIPS/eip-233.md | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/EIPS/eip-233.md b/EIPS/eip-233.md
    index daf86cbf..16eaf936 100644
    --- a/EIPS/eip-233.md
    +++ b/EIPS/eip-233.md
    @@ -17,13 +17,13 @@ Today discussions about hard forks happen at various forums and sometimes in ad-
     
     ## Specification
     
    -A Meta EIP should be created as a draft as a pull request as soon as a hard fork is planned. This EIP should contain:
    +A Meta EIP should be created and merged as a *Draft* as soon as a new hard fork is planned. This EIP should contain:
     - the desired codename of the hard fork,
     - list of all the EIPs included in the hard fork and
     - activation block number once decided and
     - the **Requires** header should point to the previous hard fork meta EIP.
     
    -The draft pull request shall be updated with summaries of the decisions around the hard fork. It should move in to the `Accepted` state once the changes are frozen (i.e. all referenced EIPs are in the `Accepted` state) and in to the `Final` state once the hard fork has been activated.
    +The draft shall be updated with summaries of the decisions around the hard fork. It should move in to the `Accepted` state once the changes are frozen (i.e. all referenced EIPs are in the `Accepted` state) and in to the `Final` state once the hard fork has been activated.
     
     The Yellow Paper must be kept updated with the hard fork changes. It could either refer to the fork codename or the EIP number of the fork. The hard fork cannot be activated before the Yellow Paper is updated.
     
    
    From ef22a93e9ba0b9332f30a43b705854e37b1ce6ef Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 20:22:44 +0100
    Subject: [PATCH 156/177] Remove the requirement of keeping the Yellow Paper up
     to date (this seemed to be the consensus)
    
    ---
     EIPS/eip-233.md | 2 --
     1 file changed, 2 deletions(-)
    
    diff --git a/EIPS/eip-233.md b/EIPS/eip-233.md
    index 16eaf936..9ec93523 100644
    --- a/EIPS/eip-233.md
    +++ b/EIPS/eip-233.md
    @@ -25,8 +25,6 @@ A Meta EIP should be created and merged as a *Draft* as soon as a new hard fork
     
     The draft shall be updated with summaries of the decisions around the hard fork. It should move in to the `Accepted` state once the changes are frozen (i.e. all referenced EIPs are in the `Accepted` state) and in to the `Final` state once the hard fork has been activated.
     
    -The Yellow Paper must be kept updated with the hard fork changes. It could either refer to the fork codename or the EIP number of the fork. The hard fork cannot be activated before the Yellow Paper is updated.
    -
     ## Rationale
     
     A meta EIP for coordinating the hard fork should help in visibility and traceability of the scope of changes as well as provide a simple name and/or number for referring to the proposed fork.
    
    From 018bbaf64a3a0a506fc53bc51e84b28a02f86723 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Fri, 24 Aug 2018 15:37:55 +0100
    Subject: [PATCH 157/177] Specify restricted address range for
     precompiles/system contracts
    
    ---
     ...draft_restrict_precompile_address_range.md | 39 +++++++++++++++++++
     1 file changed, 39 insertions(+)
     create mode 100644 EIPS/eip-draft_restrict_precompile_address_range.md
    
    diff --git a/EIPS/eip-draft_restrict_precompile_address_range.md b/EIPS/eip-draft_restrict_precompile_address_range.md
    new file mode 100644
    index 00000000..7cfb3d40
    --- /dev/null
    +++ b/EIPS/eip-draft_restrict_precompile_address_range.md
    @@ -0,0 +1,39 @@
    +---
    +eip: 
    +title: Specify restricted address range for precompiles/system contracts
    +author: Alex Beregszaszi (@axic)
    +discussions-to: 
    +status: Draft
    +type: Standards Track
    +category: Core
    +created: 2018-07-27
    +---
    +
    +## Simple Summary
    +Specify an Ethereum address range occupied by precompiles and future system contracts. Regular accounts and contracts cannot obtain such an address.
    +
    +## Abstract
    +The address range between 0x0000000000000000000000000000000000000000 and 0x00000000000000000000000000000000000000ff is reserved for precompiles and system contracts.
    +
    +## Motivation
    +This will simplify certain future features where unless this is implemented, several exceptions must be specified.
    +
    +## Specification
    +The address range between 0x0000000000000000000000000000000000000000 and 0x00000000000000000000000000000000000000ff is reserved for precompiles and system contracts.
    +
    +If a contract creation (as a result of a create transaction or a `CREATE` opcode) results in an address within this range, then it is rejected.
    +
    +## Rationale
    +N/A
    +
    +## Backwards Compatibility
    +No contracts on the main network have been created at the specified addresses. As a result it should pose no backwards compatibility problems.
    +
    +## Test Cases
    +N/A
    +
    +## Implementation
    +N/A
    +
    +## Copyright
    +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
    
    From dfe71b69e602b000a6a1e0436ccac92f0d33ab13 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Fri, 24 Aug 2018 16:37:37 +0100
    Subject: [PATCH 158/177] Add EIP number and discussion URL
    
    ---
     EIPS/eip-draft_restrict_precompile_address_range.md | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/EIPS/eip-draft_restrict_precompile_address_range.md b/EIPS/eip-draft_restrict_precompile_address_range.md
    index 7cfb3d40..ac5e3b5b 100644
    --- a/EIPS/eip-draft_restrict_precompile_address_range.md
    +++ b/EIPS/eip-draft_restrict_precompile_address_range.md
    @@ -1,8 +1,8 @@
     ---
    -eip: 
    +eip: 1352
     title: Specify restricted address range for precompiles/system contracts
    -author: Alex Beregszaszi (@axic)
    -discussions-to: 
    +author: Alex Beregszaszi (axic)
    +discussions-to: https://ethereum-magicians.org/t/eip-1352-specify-restricted-address-range-for-precompiles-system-contracts/1151
     status: Draft
     type: Standards Track
     category: Core
    
    From 3f3e4f0d0cc8912ec0e34b7d00c5c75319c51e49 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 20:02:59 +0100
    Subject: [PATCH 159/177] Rename to eip-1352.md
    
    ---
     ...eip-draft_restrict_precompile_address_range.md => eip-1352.md} | 0
     1 file changed, 0 insertions(+), 0 deletions(-)
     rename EIPS/{eip-draft_restrict_precompile_address_range.md => eip-1352.md} (100%)
    
    diff --git a/EIPS/eip-draft_restrict_precompile_address_range.md b/EIPS/eip-1352.md
    similarity index 100%
    rename from EIPS/eip-draft_restrict_precompile_address_range.md
    rename to EIPS/eip-1352.md
    
    From 497a3fb83b369f697225350fd23aa78a31e543ce Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 20:07:02 +0100
    Subject: [PATCH 160/177] Reserve 65536 addresses for precompiles
    
    ---
     EIPS/eip-1352.md | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/EIPS/eip-1352.md b/EIPS/eip-1352.md
    index ac5e3b5b..176edb0d 100644
    --- a/EIPS/eip-1352.md
    +++ b/EIPS/eip-1352.md
    @@ -13,13 +13,13 @@ created: 2018-07-27
     Specify an Ethereum address range occupied by precompiles and future system contracts. Regular accounts and contracts cannot obtain such an address.
     
     ## Abstract
    -The address range between 0x0000000000000000000000000000000000000000 and 0x00000000000000000000000000000000000000ff is reserved for precompiles and system contracts.
    +The address range between 0x0000000000000000000000000000000000000000 and 0x000000000000000000000000000000000000ffff is reserved for precompiles and system contracts.
     
     ## Motivation
     This will simplify certain future features where unless this is implemented, several exceptions must be specified.
     
     ## Specification
    -The address range between 0x0000000000000000000000000000000000000000 and 0x00000000000000000000000000000000000000ff is reserved for precompiles and system contracts.
    +The address range between 0x0000000000000000000000000000000000000000 and 0x000000000000000000000000000000000000ffff is reserved for precompiles and system contracts.
     
     If a contract creation (as a result of a create transaction or a `CREATE` opcode) results in an address within this range, then it is rejected.
     
    
    From 2f243df95e2ce60feeabcd06a9235335c1af9fda Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 20:17:21 +0100
    Subject: [PATCH 161/177] Do not require explicit checks for clashing addresses
    
    ---
     EIPS/eip-1352.md | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/EIPS/eip-1352.md b/EIPS/eip-1352.md
    index 176edb0d..b0a69a15 100644
    --- a/EIPS/eip-1352.md
    +++ b/EIPS/eip-1352.md
    @@ -21,7 +21,8 @@ This will simplify certain future features where unless this is implemented, sev
     ## Specification
     The address range between 0x0000000000000000000000000000000000000000 and 0x000000000000000000000000000000000000ffff is reserved for precompiles and system contracts.
     
    -If a contract creation (as a result of a create transaction or a `CREATE` opcode) results in an address within this range, then it is rejected.
    +Due to the extremely low probability (and lack of adequate testing possibilities) no explicit checks should be added to ensure that external transaction signing or
    +the invoking of the `CREATE` instruction can result in a precompile address.
     
     ## Rationale
     N/A
    
    From 6071075e052c017af4506a76053164164c1f4e3e Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Fri, 31 Aug 2018 21:49:04 +0100
    Subject: [PATCH 162/177] Reduced gas cost for call to self
    
    ---
     ...draft_reduced_gas_cost_for_call_to_self.md | 39 +++++++++++++++++++
     1 file changed, 39 insertions(+)
     create mode 100644 EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    
    diff --git a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    new file mode 100644
    index 00000000..854900d1
    --- /dev/null
    +++ b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    @@ -0,0 +1,39 @@
    +---
    +eip: 
    +title: Reduced gas cost for call to self
    +author: Alex Beregszaszi (@axic)
    +discussions-to: 
    +status: Draft
    +type: Standards Track
    +category: Core
    +created: 2018-08-31
    +requires: 150
    +---
    +
    +## Abstract
    +Reduce the gas cost for call instructions, when the goal is to run a new instance of the currently loaded contract.
    +
    +## Motivation
    +TBA (there's a lot to write here)
    +
    +## Specification
    +If `block.number >= FORK_BLKNUM`, then decrease the cost of `CALL`, `DELEGATECALL`, `CALLCODE` and `STATICCALL` from 700 to 40,
    +if and only if, the destination address of the call equals to the address of the caller.
    +
    +## Rationale
    +EIP150 has increased the cost of these instructions from 40 to 700 to more fairly charge for loading new contracts from disk, e.g. to reflect the I/O charge more closely.
    +By assuming that 660 is the cost of loading a contract from disk, one can assume that the original 40 gas is a fair cost of creating a new VM instance of an already loaded contract code.
    +
    +## Backwards Compatibility
    +This should pose no risk to backwards compatibility. Currently existing contracts should not notice the difference, just see cheaper execution.
    +With EIP150 contract (and language) developers had a lesson that relying on strict gas costs is not feasible as costs may change.
    +The impact of this EIP is even less that of EIP150 because the costs are changing downwards and not upwards.
    +
    +## Test Cases
    +TBA
    +
    +## Implementation
    +TBA
    +
    +## Copyright
    +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
    
    From e8b52d32f37b26162110362bb9a05bf1e6e70c2f Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 10:14:50 +0100
    Subject: [PATCH 163/177] Add motivation section by jacqueswww
    
    ---
     ...draft_reduced_gas_cost_for_call_to_self.md | 20 +++++++++++++++++--
     1 file changed, 18 insertions(+), 2 deletions(-)
    
    diff --git a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    index 854900d1..fd1af8c1 100644
    --- a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    +++ b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    @@ -1,7 +1,7 @@
     ---
     eip: 
     title: Reduced gas cost for call to self
    -author: Alex Beregszaszi (@axic)
    +author: Alex Beregszaszi (@axic), Jacques Wagener (@jacqueswww)
     discussions-to: 
     status: Draft
     type: Standards Track
    @@ -14,7 +14,23 @@ requires: 150
     Reduce the gas cost for call instructions, when the goal is to run a new instance of the currently loaded contract.
     
     ## Motivation
    -TBA (there's a lot to write here)
    +The current gas cost of 700 for all call types (`CALL`, `DELEGATECALL`, `CALLCODE` and `STATICCALL`) does not take into account that a call to a contract itself
    +does not need to perform additional I/O operations as with an external contract call, as the current contract code has already been loaded into the VM memory.
    +
    +Reducing the call-to-self gas cost would greatly benefit smart contract languages, such as Solidity and Vyper, who would then be able to utilise `CALL` instead
    +of `JUMP` opcodes for internal function calls.
    +
    +Using `JUMP` comes at a considerable cost in complexity to the implementation of a smart contract language and/or compiler. The context (including stack and memory)
    +must be swapped in and out of the calling functions context.
    +
    +Using call-to-self provides the guarentee that when making an internal call the function can rely on a clear reset state of memory or context, benefiting both
    +contract writers and contract consumers against potentially undetetected edge cases were memory could poison the context of the internal function.
    +
    +Because of the `JUMP` usage for internal functions a smart contract languages are also at risk of reaching the stack depth limit considerbly faster, if nested
    +function calls with many in and/or outputs are required.
    +
    +Reducing the gas cost, and thereby incentivising of using call-to-self instead of `JUMP`s for the internal function implementation will also benefit static
    +analyzers and tracers.
     
     ## Specification
     If `block.number >= FORK_BLKNUM`, then decrease the cost of `CALL`, `DELEGATECALL`, `CALLCODE` and `STATICCALL` from 700 to 40,
    
    From 236c2394c4bc6ffd7f9898254ec63b53d3afe04e Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 18:17:50 +0100
    Subject: [PATCH 164/177] Some additions to the motivation section
    
    ---
     EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md | 8 +++++---
     1 file changed, 5 insertions(+), 3 deletions(-)
    
    diff --git a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    index fd1af8c1..c4718617 100644
    --- a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    +++ b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    @@ -15,13 +15,15 @@ Reduce the gas cost for call instructions, when the goal is to run a new instanc
     
     ## Motivation
     The current gas cost of 700 for all call types (`CALL`, `DELEGATECALL`, `CALLCODE` and `STATICCALL`) does not take into account that a call to a contract itself
    -does not need to perform additional I/O operations as with an external contract call, as the current contract code has already been loaded into the VM memory.
    +does not need to perform additional I/O operations, because the current contract code has already been loaded into memory.
     
     Reducing the call-to-self gas cost would greatly benefit smart contract languages, such as Solidity and Vyper, who would then be able to utilise `CALL` instead
    -of `JUMP` opcodes for internal function calls.
    +of `JUMP` opcodes for internal function calls. While languages can already utilise `CALL` for internal function calls, they are discouraged to do so due to the
    +gas costs associated with it.
     
     Using `JUMP` comes at a considerable cost in complexity to the implementation of a smart contract language and/or compiler. The context (including stack and memory)
    -must be swapped in and out of the calling functions context.
    +must be swapped in and out of the calling functions context. A desired feature is having *pure* functions, which do not modify the state of memory, and realising
    +them through `JUMP` requires a bigger effort from the compiler as opposed to being able to use `CALL`s.
     
     Using call-to-self provides the guarentee that when making an internal call the function can rely on a clear reset state of memory or context, benefiting both
     contract writers and contract consumers against potentially undetetected edge cases were memory could poison the context of the internal function.
    
    From dfc218e3fac7e7d8b3d0c621d34effca2c935d09 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 18:33:05 +0100
    Subject: [PATCH 165/177] Add EIP number and discussion URL
    
    ---
     EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    index c4718617..87a29aad 100644
    --- a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    +++ b/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    @@ -1,8 +1,8 @@
     ---
    -eip: 
    +eip: 1380
     title: Reduced gas cost for call to self
     author: Alex Beregszaszi (@axic), Jacques Wagener (@jacqueswww)
    -discussions-to: 
    +discussions-to: https://ethereum-magicians.org/t/eip-1380-reduced-gas-cost-for-call-to-self/1242
     status: Draft
     type: Standards Track
     category: Core
    
    From 8030c5b61c9ce18492dd3119d9709371e3879da8 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Tue, 4 Sep 2018 18:45:24 +0100
    Subject: [PATCH 166/177] Rename to eip-1380.md
    
    ---
     ...eip-draft_reduced_gas_cost_for_call_to_self.md => eip-1380.md} | 0
     1 file changed, 0 insertions(+), 0 deletions(-)
     rename EIPS/{eip-draft_reduced_gas_cost_for_call_to_self.md => eip-1380.md} (100%)
    
    diff --git a/EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md b/EIPS/eip-1380.md
    similarity index 100%
    rename from EIPS/eip-draft_reduced_gas_cost_for_call_to_self.md
    rename to EIPS/eip-1380.md
    
    From c7dbaefcc9bff51b689582c6c8d8950561d0114f Mon Sep 17 00:00:00 2001
    From: Nick Mudge 
    Date: Tue, 18 Sep 2018 22:43:49 -0700
    Subject: [PATCH 167/177] Automatically merged updates to draft EIP(s) 998
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-998.md | 4 ++++
     1 file changed, 4 insertions(+)
    
    diff --git a/EIPS/eip-998.md b/EIPS/eip-998.md
    index 1f8bab05..88f0b920 100644
    --- a/EIPS/eip-998.md
    +++ b/EIPS/eip-998.md
    @@ -1312,6 +1312,10 @@ Here is an example of what could occur if **from** was not explicitly provided i
     
     
     ### Additional Reading Material
    +[Introducing Crypto Composables](https://medium.com/coinmonks/introducing-crypto-composables-ee5701fde217)
    +
    +[Crypto Composables — Building Blocks and Applications](https://medium.com/coinmonks/introducing-crypto-composables-ee5701fde217)
    +
     [Top-Down and Bottom-Up Composables, What's the Difference and Which One Should You Use?](https://hackernoon.com/top-down-and-bottom-up-composables-whats-the-difference-and-which-one-should-you-use-db939f6acf1d)
     
     Join the discussion about composables in the [NFTy Magician's Discord](https://discord.gg/8cuuj2Y).
    
    From 5ffc3dd2a814a85050b929e9a349ccc0582a3877 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Wed, 19 Sep 2018 13:16:29 +0100
    Subject: [PATCH 168/177] Automatically merged updates to draft EIP(s) 233
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-233.md | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/EIPS/eip-233.md b/EIPS/eip-233.md
    index 9ec93523..dd6877f2 100644
    --- a/EIPS/eip-233.md
    +++ b/EIPS/eip-233.md
    @@ -2,6 +2,7 @@
     eip: 233
     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
     created: 2017-03-23
    
    From 48a426f20b20a76632a9e64115e31a8384189db9 Mon Sep 17 00:00:00 2001
    From: Jacques Dafflon 
    Date: Wed, 19 Sep 2018 15:43:11 +0200
    Subject: [PATCH 169/177] ERC820: Moving to last call (#1427)
    
    ---
     EIPS/eip-820.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/EIPS/eip-820.md b/EIPS/eip-820.md
    index ae6a5b85..8dcb92c9 100644
    --- a/EIPS/eip-820.md
    +++ b/EIPS/eip-820.md
    @@ -3,7 +3,7 @@ eip: 820
     title: Pseudo-introspection Registry Contract
     author: Jordi Baylina , Jacques Dafflon 
     discussions-to: https://github.com/ethereum/EIPs/issues/820
    -status: Draft
    +status: Last Call
     type: Standards Track
     category: ERC
     created: 2018-01-05
    
    From 0259d7760a876d199b8cbdcff103013d8b77a4a5 Mon Sep 17 00:00:00 2001
    From: Pedro Gomes 
    Date: Wed, 19 Sep 2018 07:53:04 -0700
    Subject: [PATCH 170/177] Edit & Typos (ERC-1328) (#1371)
    
    * fix typo author header
    
    * Update eip-1328.md
    
    * change sessionId capitalization
    ---
     EIPS/eip-1328.md | 12 +++++++++---
     1 file changed, 9 insertions(+), 3 deletions(-)
    
    diff --git a/EIPS/eip-1328.md b/EIPS/eip-1328.md
    index 90fb6d21..4142fa96 100644
    --- a/EIPS/eip-1328.md
    +++ b/EIPS/eip-1328.md
    @@ -1,7 +1,7 @@
     ---
     eip: 1328
     title: WalletConnect Standard URI Format
    -authors: ligi , Pedro Gomes 
    +author: ligi , Pedro Gomes 
     type: Standards Track
     category: ERC
     status: Draft
    @@ -24,8 +24,8 @@ This standard defines how the data to connect some application and a wallet can
     
     Function call URIs follow the ERC-831 URI format, with the following parameters:
     
    -    request       = "ethereum" ":" [ "wc-" ]sessionID [ "@" version ][ "?" parameters ]
    -    sessionID     = STRING
    +    request       = "ethereum" ":" [ "wc-" ]sessionId [ "@" version ][ "?" parameters ]
    +    sessionId     = STRING
         version       = 1*DIGIT
         parameters    = parameter *( "&" parameter )
         parameter     = key "=" value
    @@ -36,6 +36,12 @@ Function call URIs follow the ERC-831 URI format, with the following parameters:
     
     Required parameters are dependent on the WalletConnect standard version which currently is specified to only include mobile-to-desktop connection sessions which only require `name` which describes the dapp name, `bridge` which includes the bridge URL, `symKey` which includes the symmetric key in base64.
     
    +### Example
    +
    +```
    +ethereum:wc-8a5e5bdc-a0e4-4702-ba63-8f1a5655744f@1?name=DappExample&bridge=https://bridge.example.com&symKey=KzpSTk1pezg5eTJRNmhWJmoxdFo6UDk2WlhaOyQ5N0U=
    +```
    +
     ## Rationale
     
     The need for this ERC stems from the discussion to move away from JSON format used in current beta version of the WalletConnect standard 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 for other compatible EIPs using the ERC-831 URI format for Ethereum. Also by using a URI instead of a JSON inside the QR-Code the Android Intent system can be leveraged.
    
    From f32661d199793356b0c24efad2bdbab68678e2e6 Mon Sep 17 00:00:00 2001
    From: Afri Schoedon <5chdn@users.noreply.github.com>
    Date: Thu, 20 Sep 2018 00:58:33 +0200
    Subject: [PATCH 171/177] Automatically merged updates to draft EIP(s) 1013
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-1013.md | 9 ++++-----
     1 file changed, 4 insertions(+), 5 deletions(-)
    
    diff --git a/EIPS/eip-1013.md b/EIPS/eip-1013.md
    index 63de5905..a6d3fe62 100644
    --- a/EIPS/eip-1013.md
    +++ b/EIPS/eip-1013.md
    @@ -5,7 +5,7 @@ author: Nick Savers (@nicksavers)
     type: Meta
     status: Draft
     created: 2018-04-20
    -requires: 145, 210, 1014, 1052, 1087
    +requires: 145, 1014, 1052, 1234, 1283
     ---
     
     ## Abstract
    @@ -21,15 +21,14 @@ This meta-EIP specifies the changes included in the Ethereum hardfork named Cons
       - `Block >= TBD` on the Ropsten testnet
     - Included EIPs:
       - [EIP 145](./eip-145.md): Bitwise shifting instructions in EVM
    -  - [EIP 210](./eip-210.md): Blockhash refactoring
       - [EIP 1014](./eip-1014.md): Skinny CREATE2
       - [EIP 1052](./eip-1052.md): EXTCODEHASH Opcode
    -  - [EIP 1087](./eip-1087.md): Net gas metering for SSTORE operations
    -  - TBD: Ice-age delay
    +  - [EIP 1234](./eip-1234.md): Delay difficulty bomb, adjust block reward
    +  - [EIP 1283](./eip-1283.md): Net gas metering for SSTORE without dirty maps
     
     ## References
     
    -The list above includes the EIPs discussed as candidates for Constantinople at the [All Core Dev Meeting #42](https://github.com/ethereum/pm/blob/c78d7ff147daaafa4dce60ba56cdedbe268a0488/All%20Core%20Devs%20Meetings/Meeting%2042.md#constantinople-hard-fork-and-timing). Final decision is planned for the subsequent meeting #43.
    +The list above includes the EIPs discussed as candidates for Constantinople at the All Core Dev [Constantinople Session #1](https://github.com/ethereum/pm/issues/55). See also [Constantinople Progress Tracker](https://github.com/ethereum/pm/issues/53).
     
     ## Copyright
     
    
    From a15edb61bbf564cfe61b3dcaa42150d6a93401c8 Mon Sep 17 00:00:00 2001
    From: Alex Beregszaszi 
    Date: Wed, 19 Sep 2018 00:26:06 +0100
    Subject: [PATCH 172/177] Add @axic username to my EIPs
    
    ---
     EIPS/eip-1352.md | 2 +-
     EIPS/eip-140.md  | 2 +-
     EIPS/eip-141.md  | 2 +-
     EIPS/eip-145.md  | 2 +-
     EIPS/eip-606.md  | 2 +-
     EIPS/eip-607.md  | 2 +-
     EIPS/eip-608.md  | 2 +-
     EIPS/eip-609.md  | 2 +-
     8 files changed, 8 insertions(+), 8 deletions(-)
    
    diff --git a/EIPS/eip-1352.md b/EIPS/eip-1352.md
    index b0a69a15..11416bba 100644
    --- a/EIPS/eip-1352.md
    +++ b/EIPS/eip-1352.md
    @@ -1,7 +1,7 @@
     ---
     eip: 1352
     title: Specify restricted address range for precompiles/system contracts
    -author: Alex Beregszaszi (axic)
    +author: Alex Beregszaszi (@axic)
     discussions-to: https://ethereum-magicians.org/t/eip-1352-specify-restricted-address-range-for-precompiles-system-contracts/1151
     status: Draft
     type: Standards Track
    diff --git a/EIPS/eip-140.md b/EIPS/eip-140.md
    index 744db814..8469cb07 100644
    --- a/EIPS/eip-140.md
    +++ b/EIPS/eip-140.md
    @@ -1,7 +1,7 @@
     ---
     eip: 140
     title: REVERT instruction
    -author: Alex Beregszaszi, Nikolai Mushegian 
    +author: Alex Beregszaszi (@axic), Nikolai Mushegian 
     type: Standards Track
     category: Core
     status: Final
    diff --git a/EIPS/eip-141.md b/EIPS/eip-141.md
    index 64b6734f..2cc65835 100644
    --- a/EIPS/eip-141.md
    +++ b/EIPS/eip-141.md
    @@ -1,7 +1,7 @@
     ---
     eip: 141
     title: Designated invalid EVM instruction
    -author: Alex Beregszaszi
    +author: Alex Beregszaszi (@axic)
     type: Standards Track
     category: Core
     status: Final
    diff --git a/EIPS/eip-145.md b/EIPS/eip-145.md
    index d911f0f7..67eacc2e 100644
    --- a/EIPS/eip-145.md
    +++ b/EIPS/eip-145.md
    @@ -1,7 +1,7 @@
     ---
     eip: 145
     title: Bitwise shifting instructions in EVM
    -author: Alex Beregszaszi, Paweł Bylica
    +author: Alex Beregszaszi (@axic), Paweł Bylica
     type: Standards Track
     category: Core
     status: Accepted
    diff --git a/EIPS/eip-606.md b/EIPS/eip-606.md
    index d7935a8b..9273e9eb 100644
    --- a/EIPS/eip-606.md
    +++ b/EIPS/eip-606.md
    @@ -1,7 +1,7 @@
     ---
     eip: 606
     title: "Hardfork Meta: Homestead"
    -author: Alex Beregszaszi
    +author: Alex Beregszaszi (@axic)
     type: Meta
     status: Final
     created: 2017-04-23
    diff --git a/EIPS/eip-607.md b/EIPS/eip-607.md
    index 6d24572d..5c524c37 100644
    --- a/EIPS/eip-607.md
    +++ b/EIPS/eip-607.md
    @@ -1,7 +1,7 @@
     ---
     eip: 607
     title: "Hardfork Meta: Spurious Dragon"
    -author: Alex Beregszaszi
    +author: Alex Beregszaszi (@axic)
     type: Meta
     status: Final
     created: 2017-04-23
    diff --git a/EIPS/eip-608.md b/EIPS/eip-608.md
    index 9d056b0e..5c5060d8 100644
    --- a/EIPS/eip-608.md
    +++ b/EIPS/eip-608.md
    @@ -1,7 +1,7 @@
     ---
     eip: 608
     title: "Hardfork Meta: Tangerine Whistle"
    -author: Alex Beregszaszi
    +author: Alex Beregszaszi (@axic)
     type: Meta
     status: Final
     created: 2017-04-23
    diff --git a/EIPS/eip-609.md b/EIPS/eip-609.md
    index bfdd5069..94a708c4 100644
    --- a/EIPS/eip-609.md
    +++ b/EIPS/eip-609.md
    @@ -1,7 +1,7 @@
     ---
     eip: 609
     title: "Hardfork Meta: Byzantium"
    -author: Alex Beregszaszi
    +author: Alex Beregszaszi (@axic)
     type: Meta
     status: Final
     created: 2017-04-23
    
    From ccca01c77be41ff3e717deb2f3372c6daaeb8725 Mon Sep 17 00:00:00 2001
    From: Philippe Castonguay 
    Date: Thu, 20 Sep 2018 18:02:23 -0400
    Subject: [PATCH 173/177] Automatically merged updates to draft EIP(s) 1271
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-1271.md | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/EIPS/eip-1271.md b/EIPS/eip-1271.md
    index 95546d33..2e9b159b 100644
    --- a/EIPS/eip-1271.md
    +++ b/EIPS/eip-1271.md
    @@ -43,11 +43,12 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S
     *
     * MUST return a bool upon valid or invalid signature with corresponding _data
     * MUST take (bytes, bytes) as arguments
    +* MUST allow external calls
     */ 
     function isValidSignature(
    -	bytes _data, 
    +    bytes _data, 
         bytes _signature)
    -	public
    +    public
         view 
         returns (bool isValid); 
     ```
    
    From 5ebb523d28cacbef15bf4fd35190ee27e8dfbe48 Mon Sep 17 00:00:00 2001
    From: Brooklyn Zelenka 
    Date: Sun, 23 Sep 2018 18:41:08 -0700
    Subject: [PATCH 174/177] Automatically merged updates to draft EIP(s) 1066
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-1066.md | 44 +++++++++++++++++++++++++-------------------
     1 file changed, 25 insertions(+), 19 deletions(-)
    
    diff --git a/EIPS/eip-1066.md b/EIPS/eip-1066.md
    index fd09ad09..f0647e11 100644
    --- a/EIPS/eip-1066.md
    +++ b/EIPS/eip-1066.md
    @@ -105,7 +105,7 @@ explanatory and layout reasons.
     
     Unspecified codes are _not_ free for arbitrary use, but rather open for further specification.
     
    -#### Generic
    +#### `0x0*` Generic
     
     General codes. These double as bare "reasons", since `0x01 == 1`.
     
    @@ -128,7 +128,7 @@ General codes. These double as bare "reasons", since `0x01 == 1`.
     | `0x0E`          |                         |
     | `0x0F`          | Meta or Info Only       |
     
    -#### Permission
    +#### `0x1*` Permission
     
     Related to permisson, authorization, approval, and so on.
     
    @@ -151,7 +151,7 @@ Related to permisson, authorization, approval, and so on.
     | `0x1E`          |                          |
     | `0x1F`          | Permission Meta or Info  |
     
    -#### Find, Match, &c
    +#### `0x2*` Find, Match, &c
     
     This range is broadly intended for finding and matching.
     Data lookups and order matching are two common use cases.
    @@ -175,7 +175,7 @@ Data lookups and order matching are two common use cases.
     | `0x2E`          |                          |
     | `0x2F`          | Matching Meta or Info    |
     
    -#### Negotiation, Terms, and Offers
    +#### `0x3*` Negotiation, Terms, and Offers
     
     Negotiation, and very broadly the flow of such transactions.
     Note that "other party" may be more than one actor (not necessarily the sender).
    @@ -199,7 +199,7 @@ Note that "other party" may be more than one actor (not necessarily the sender).
     | `0x3E`          |                             |
     | `0x3F`          | Negotiation Meta or Info    |
     
    -#### Availability
    +#### `0x4*` Availability
     
     Service or action availability.
     
    @@ -222,27 +222,27 @@ Service or action availability.
     | `0x4E`          |                             |
     | `0x4F`          | Availability Meta or Info   |
     
    -#### `0x5_` TBD
    +#### `0x5*` TBD
     
     Currently unspecified
     
    -#### `0x6_` TBD
    +#### `0x6*` TBD
     
     Currently unspecified
     
    -#### `0x7_` TBD
    +#### `0x7*` TBD
     
     Currently unspecified
     
    -#### `0x8_` TBD
    +#### `0x8*` TBD
     
     Currently unspecified
     
    -#### `0x9_` TBD
    +#### `0x9*` TBD
     
     Currently unspecified
     
    -#### Application-Specific Codes
    +#### `0xA*` Application-Specific Codes
     
     Contracts may have special states that they need to signal.
     This proposal only outlines the broadest meanings, but implementers may have very
    @@ -267,19 +267,19 @@ specific meanings for each, as long as they are coherent with the broader defini
     | `0xAE`          |                                 |
     | `0xAF`          | App-Specific Meta or Info       |
     
    -#### `0xB_` TBD
    +#### `0xB*` TBD
     
     Currently unspecified
     
    -#### `0xC_` TBD
    +#### `0xC*` TBD
     
     Currently unspecified
     
    -#### `0xD_` TBD
    +#### `0xD*` TBD
     
     Currently unspecified
     
    -#### Cryptography and Authentication
    +#### `0xE*` Cryptography and Authentication
     
     Actions around signatures, cryptography, signing, and application-level authentication.
     
    @@ -307,7 +307,7 @@ or process used.
     
     #### `0xF0` Off-Chain
     
    -For off-chain actions. Much like th `0x0_: Generic` range, `0xF_` is very general,
    +For off-chain actions. Much like th `0x0_: Generic` range, `0xF*` is very general,
     and does little to modify the reason.
     
     Among other things, the meta code `0xFF` may be used to describe what the off-chain process is.
    @@ -489,7 +489,7 @@ most forwards-compatible method of transmission is as the first value of a multi
     
     Familiarity is also a motivating factor. A consistent position and encoding together
     follow the principle of least surprise. It is both viewable as a "header" in the HTTP analogy,
    -or like the "tag" in BEAM tagged tupples.
    +or like the "tag" in BEAM tagged tuples.
     
     ### Human Readable
     
    @@ -500,7 +500,13 @@ Cognitive load is lowered by organizing the table into categories and reasons.
     While this repository includes helper enums, we have found working directly in
     the hex values to be quite natural. ESC `0x10` is just as comfortable as HTTP 401, for example.
     
    -### Extensiblilty
    +#### Localizations
    +
    +One commonly requested application of this spec is human-readable translations
    +of codes. This has been moved to its own proposal: [ERC-1444](https://github.com/ethereum/EIPs/pull/1444/),
    +primarily due to a desire to keep both specs focused.
    +
    +### Extensibility
     
     The `0xA` category is reserved for application-specific statuses.
     In the case that 256 codes become insufficient, `bytes1` may be embedded in larger byte arrays.
    @@ -526,7 +532,7 @@ but becomes very natural after a couple hours of use.
     
     #### Short Forms
     
    -Generic is `0x0_`, general codes are consistent with their integer representations
    +Generic is `0x0*`, general codes are consistent with their integer representations
     
     ```solidity
     hex"1" == hex"01" == 1 // with casting
    
    From 9991f4689bcbbb0325797e1344b44a76341d2ec0 Mon Sep 17 00:00:00 2001
    From: Ivo Georgiev 
    Date: Mon, 24 Sep 2018 09:31:59 +0100
    Subject: [PATCH 175/177] Automatically merged updates to draft EIP(s) 712
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-712.md | 12 ++++++------
     1 file changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/EIPS/eip-712.md b/EIPS/eip-712.md
    index f9cdb936..17f6fad1 100644
    --- a/EIPS/eip-712.md
    +++ b/EIPS/eip-712.md
    @@ -375,19 +375,19 @@ function hashStruct(Mail memory mail) pure returns (bytes32 hash) {
     
         assembly {
             // Back up select memory
    -        let temp1 := mload(sub(order, 32))
    -        let temp2 := mload(add(order, 128))
    +        let temp1 := mload(sub(mail, 32))
    +        let temp2 := mload(add(mail, 128))
     
             // Write typeHash and sub-hashes
             mstore(sub(mail, 32), typeHash)
    -        mstore(add(order, 64), contentsHash)
    +        mstore(add(mail, 64), contentsHash)
     
             // Compute hash
    -        hash := keccak256(sub(order, 32), 128)
    +        hash := keccak256(sub(mail, 32), 128)
     
             // Restore memory
    -        mstore(sub(order, 32), temp1)
    -        mstore(add(order, 64), temp2)
    +        mstore(sub(mail, 32), temp1)
    +        mstore(add(mail, 64), temp2)
         }
     }
     ```
    
    From 6965fff12caaf1cf237ee5c92e9782965e4b1eb7 Mon Sep 17 00:00:00 2001
    From: Muhammad Altabba 
    Date: Mon, 24 Sep 2018 14:18:21 +0300
    Subject: [PATCH 176/177] Automatically merged updates to draft EIP(s) 725
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-725.md | 28 ++++++++++++++--------------
     1 file changed, 14 insertions(+), 14 deletions(-)
    
    diff --git a/EIPS/eip-725.md b/EIPS/eip-725.md
    index 94106089..71371f0a 100644
    --- a/EIPS/eip-725.md
    +++ b/EIPS/eip-725.md
    @@ -10,14 +10,14 @@ created: 2017-10-02
     ---
     
     ## Simple Summary
    -Proxy contract for key management and execution, to establish a Blockchain identity.
    +A proxy contract for key management and execution, to establish a Blockchain identity.
     
     ## Abstract
     The following describes standard functions for a unique identity for humans, groups, objects and machines.
    -This identity can hold keys to sign actions (transactions, documents, logins, access, etc), and claims, which are attested from third parties (issuers) and self attested ([#ERC735](https://github.com/ethereum/EIPs/issues/735)), as well as a proxy function to act directly on the blockchain.
    +This identity can hold keys to sign actions (transactions, documents, logins, access, etc), and claims, which are attested from third parties (issuers) and self-attested ([#ERC735](https://github.com/ethereum/EIPs/issues/735)), as well as a proxy function,  to act directly on the blockchain.
     
     ## Motivation
    -This standardised identity interface will allow Dapps, smart contracts and thirdparties to check the validity of a person, organisation, object or machine through 2 steps as described in the function XXX. Trust is here transfered to the issuers of claims.
    +This standardized identity interface will allow Dapps, smart contracts and third parties to check the validity of a person, organization, object or machine through 2 steps as described in the function XXX. Trust is here transferred to the issuers of claims.
     
     The most important functions to verify an identity are: `XXX`
     
    @@ -26,7 +26,7 @@ The most important functions to manage an identity are: `XXX`
     
     ## Definitions
     
    -- `keys`: Keys are public keys from either external accounts, or contract addresses.
    +- `keys`: Keys are public keys from either external accounts, or contracts' addresses.
     - `claim issuer`: is another smart contract or external account, which issues claims about this identity. The claim issuer can be an identity contract itself.
     - `claim`: For details about claims see [#ERC735](https://github.com/ethereum/EIPs/issues/735)
     
    @@ -64,7 +64,7 @@ function getKey(bytes32 _key) constant returns(uint256[] purposes, uint256 keyTy
     
     #### keyHasPurpose
     
    -Returns the `TRUE` if a key has is present and has the given purpose. If key is not present it returns `FALSE`.
    +Returns the `TRUE` if a key has is present and has the given purpose. If the key is not present it returns `FALSE`.
     
     ``` js
     function keyHasPurpose(bytes32 _key, uint256 purpose) constant returns(bool exists);
    @@ -82,11 +82,11 @@ function getKeysByPurpose(uint256 _purpose) constant returns(bytes32[] keys);
     
     #### addKey
     
    -Adds a `_key` to the identity. The `_purpose` specifies the purpose of key. Initially we propose four purposes:
    +Adds a `_key` to the identity. The `_purpose` specifies the purpose of the key. Initially, we propose four purposes:
     
     - `1`: MANAGEMENT keys, which can manage the identity
     - `2`: ACTION keys, which perform actions in this identities name (signing, logins, transactions, etc.)
    -- `3`: CLAIM signer keys, used to sign claims on other identities which need to be revokable.
    +- `3`: CLAIM signer keys, used to sign claims on other identities which need to be revocable.
     - `4`: ENCRYPTION keys, used to encrypt data e.g. hold in claims.
     
     MUST only be done by keys of purpose `1`, or the identity itself. If its the identity itself, the approval process will determine its approval.
    @@ -121,9 +121,9 @@ function removeKey(bytes32 _key, uint256 _purpose) returns (bool success)
     Executes an action on other contracts, or itself, or a transfer of ether.
     SHOULD require `approve` to be called with one or more keys of purpose `1` or `2` to approve this execution.
     
    -Execute COULD be used as the only accessors for `addKey`, `removeKey` and `replaceKey` and `removeClaim`.
    +Execute COULD be used as the only accessor for `addKey`, `removeKey` and `replaceKey` and `removeClaim`.
     
    -**Returns `executionId`:** SHOULD be send to the `approve` function, to approve or reject this execution.
    +**Returns `executionId`:** SHOULD be sent to the `approve` function, to approve or reject this execution.
     
     **Triggers Event:** [ExecutionRequested](#executionrequested)
     **Triggers on direct execution Event:** [Executed](#executed)
    @@ -136,8 +136,8 @@ function execute(address _to, uint256 _value, bytes _data) returns (uint256 exec
     #### approve
     
     Approves an execution or claim addition.
    -This SHOULD require `n` of `m` approvals of keys purpose `1`, if the `_to` of the execution is the identity contract itself, to successfull approve an execution.
    -And COULD require `n` of `m` approvals of keys purpose `2`, if the `_to` of the execution is another contract, to successfull approve an execution.
    +This SHOULD require `n` of `m` approvals of keys purpose `1`, if the `_to` of the execution is the identity contract itself, to successfully approve an execution.
    +And COULD require `n` of `m` approvals of keys purpose `2`, if the `_to` of the execution is another contract, to successfully approve an execution.
     
     **Triggers Event:** [Approved](#approved)
     **Triggers on successfull execution Event:** [Executed](#executed)
    @@ -159,7 +159,7 @@ Requires: [ERC 735](https://github.com/ethereum/EIPs/issues/735)
     
     #### addClaim
     
    -This SHOULD create a pending claim, which SHOULD to be approved or rejected by `n` of `m` `approve` calls from keys of purpose `1`.
    +This SHOULD create a pending claim, which SHOULD be approved or rejected by `n` of `m` `approve` calls from keys of purpose `1`.
     
     Only Events:
     **Triggers if the claim is new Event and approval process exists:** [ClaimRequested](#claimrequested)
    @@ -241,9 +241,9 @@ MUST be triggered when `approve` was called and the claim was successfully added
     
     
     ## Rationale
    -This specification was chosen to allow most flexibility and experimention around identity. By having each identity in a separate contract it allows for cross identity compatibility, but at the same time extra and altered functionality for new use cases.
    +This specification was chosen to allow most flexibility and experimentation around identity. By having each identity in a separate contract it allows for cross identity compatibility, but at the same time extra and altered functionality for new use cases.
     
    -The main critic of this standard is the verification where each identity that issues a claim, also should have a separate CLAIM signing key attached. While [#ERC780](https://github.com/ethereum/EIPs/issues/780) uses a standardised registry to assign claims to addresses.
    +The main critic of this standard is the verification where each identity that issues a claim, also should have a separate CLAIM signing key attached. While [#ERC780](https://github.com/ethereum/EIPs/issues/780) uses a standardized registry to assign claims to addresses.
     Both systems could work in conjunction and should be explored.
     While also off-chain claims using DID verifiable claims and merkle tries can be added as claims and should be explored.
     
    
    From e270bb6b79190f02e8d89f0c34deaa43a4c00fc3 Mon Sep 17 00:00:00 2001
    From: Sho IIZUKA 
    Date: Tue, 25 Sep 2018 22:45:23 +0900
    Subject: [PATCH 177/177] Automatically merged updates to draft EIP(s) 918
    
    Hi, I'm a bot! This change was automatically merged because:
    
     - It only modifies existing Draft or Last Call EIP(s)
     - The PR was approved or written by at least one author of each modified EIP
     - The build is passing
    ---
     EIPS/eip-918.md | 7 +++----
     1 file changed, 3 insertions(+), 4 deletions(-)
    
    diff --git a/EIPS/eip-918.md b/EIPS/eip-918.md
    index d6198d42..c54ab4cb 100644
    --- a/EIPS/eip-918.md
    +++ b/EIPS/eip-918.md
    @@ -18,7 +18,7 @@ This specification describes a method for initially locking tokens within a toke
     
     ### Motivation
     
    -Token distribution via the ICO model and it's derivatives is susceptable to illicit behavior by human actors. Furthermore, new token projects are centralized because a single entity must handle and control all of the initial coins and all of the the raised ICO money.  By distributing tokens via an 'Initial Mining Offering' (or IMO), the ownership of the token contract no longer belongs with the deployer at all and the deployer is 'just another user.' As a result, investor risk exposure utilizing a mined token distribution model is significantly diminished. This standard is intended to be standalone, allowing maximum interoperability with ERC20, ERC721, and others.
    +Token distribution via the ICO model and it's derivatives is susceptible to illicit behavior by human actors. Furthermore, new token projects are centralized because a single entity must handle and control all of the initial coins and all of the raised ICO money.  By distributing tokens via an 'Initial Mining Offering' (or IMO), the ownership of the token contract no longer belongs with the deployer at all and the deployer is 'just another user.' As a result, investor risk exposure utilizing a mined token distribution model is significantly diminished. This standard is intended to be standalone, allowing maximum interoperability with ERC20, ERC721, and others.
     
     ### Specification
     
    @@ -216,8 +216,7 @@ function getChallengeNumber() public view returns (bytes32);
     
     #### getMiningDifficulty
     
    -The number of digits that the digest of the PoW solution requires which typically auto adjusts during reward generation.Return the current reward amount. Depending on the algorithm, typically rewards are divided every reward era as tokens are mined to provide scarcity.
    -
    +The number of digits that the digest of the PoW solution requires which typically auto adjusts during reward generation.
     
     ``` js
     function getMiningDifficulty() public view returns (uint)
    @@ -246,7 +245,7 @@ Once the nonce and hash1 are found, these are used to call the mint() function o
     
     ### Rationale
     
    -A keccak256 algoritm does not have to be used, but it is recommended since it is a cost effective one-way algorithm to perform in the EVM and simple to perform in solidity. The nonce is the solution that miners try to find and so it is part of the hashing algorithm. A challengeNumber is also part of the hash so that future blocks cannot be mined since it acts like a random piece of data that is not revealed until a mining round starts. The msg.sender address is part of the hash so that a nonce solution is valid only for a particular Ethereum account and so the solution is not susceptible to man-in-the-middle attacks. This also allows pools to operate without being easily cheated by the miners since pools can force miners to mine using the pool's address in the hash algo.  
    +A keccak256 algorithm does not have to be used, but it is recommended since it is a cost effective one-way algorithm to perform in the EVM and simple to perform in solidity. The nonce is the solution that miners try to find and so it is part of the hashing algorithm. A challengeNumber is also part of the hash so that future blocks cannot be mined since it acts like a random piece of data that is not revealed until a mining round starts. The msg.sender address is part of the hash so that a nonce solution is valid only for a particular Ethereum account and so the solution is not susceptible to man-in-the-middle attacks. This also allows pools to operate without being easily cheated by the miners since pools can force miners to mine using the pool's address in the hash algo.  
     
     The economics of transferring electricity and hardware into mined token assets offers a flourishing community of decentralized miners the option to be involved in the Ethereum token economy directly. By voting with hashpower, an economically pegged asset to real-world resources, miners are incentivized to participate in early token trade to revamp initial costs, providing a bootstrapped stimulus mechanism between miners and early investors.