--- eip: 1066 title: Status Codes author: Brooklyn Zelenka (@expede), Tom Carchrae (@carchrae), Gleb Naumenko (@naumenkogs) discussions-to: https://ethereum-magicians.org/t/erc-1066-ethereum-status-codes-esc/ status: Draft type: Standards Track category: ERC created: 2018-05-05 --- ## Simple Summary Broadly applicable status codes for Ethereum smart contracts. ## Abstract This standard outlines a common set of Ethereum status codes (ESC) in the same vein as HTTP statuses. This provides a shared set of signals to allow smart contracts to react to situations autonomously, expose localized error messages to users, and so on. The current state of the art is to either `revert` and require human intervention, or return a Boolean pass/fail status. Status codes are similar-but-orthogonal to `revert`ing with a reason, but aimed at automation and translation. As is the case with HTTP, having a standard set of known codes has many benefits for developers. They remove friction from needing to develop your own schemes for every contract, makes inter-contract automation easier, and makes it easier to broadly understand which of the finite states your request produced. Importantly, it makes it much easier to distinguish between expected errors states, and truly exceptional conditions that require halting execution. ## Motivation ### Autonomy Smart contracts are largely intended to be autonomous. While each contract may define a specific interface, having a common set of semantic codes can help developers write code that can react appropriately to various situations. ### Semantic Density HTTP status codes are widely used for this purpose. BEAM languages use atoms and tagged tuples to signify much the same information. Both provide a lot of information both to the programmer (debugging for instance), and to the program that needs to decide what to do next. ESCs convey a much richer set of information than Booleans, and are able to be reacted to autonomously unlike arbitrary strings. ### User Feedback Since status codes are finite and known in advance, we can provide global, human-readable sets of status messages. These may also be translated into any language, differing levels of technical detail, added as `revert` messages, natspecs, and so on. We also see a desire for this [in transactions](http://eips.ethereum.org/EIPS/eip-658), and there's no reason that ESCs couldn't be used by the EVM itself. ### More than Pass/Fail While clearly related, status codes are complementary to "revert with reason". ESCs are not limited to rolling back the transaction, and may represent known error states without halting execution. They may also represent off-chain conditions, supply a string to revert, signal time delays, and more. ## Specification ### Format Codes are returned as the first value of potentially multiple return values. ```solidity // Status only function isInt(uint num) public pure returns (byte status) { return hex"01"; } // Status and value uint8 private counter; function safeIncrement(uint8 interval) public returns (byte status, uint8 newCounter) { uint8 updated = counter + interval; if (updated >= counter) { counter = updated; return (hex"01", updated); } else { return (hex"00", counter); } } ``` In the rare case that there a multiple codes required to express an idea, they should be organized in asending 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. Unspecified codes are _not_ free for arbitrary use, but rather open for further specification. #### Generic General codes. These double as bare "reasons", since `0x01 == 1`. | Code | Description | |-----------------|:------------------------| | `0x00` | Failure | | `0x01` | Success | | `0x02` | Accepted / Started | | `0x03` | Awaiting / Before | | `0x04` | Action Required | | `0x05` | Expired | | `0x06` | | | `0x07` | | | `0x08` | | | `0x09` | | | `0x0A` | | | `0x0B` | | | `0x0C` | | | `0x0D` | | | `0x0E` | | | `0x0F` | Meta or Info Only | #### Permission Related to permisson, authorization, approval, and so on. | Code | Description | |-----------------|:-------------------------| | `0x10` | Disallowed | | `0x11` | Allowed | | `0x12` | Requested Permission | | `0x13` | Awaiting Permission | | `0x14` | Awaiting Your Permission | | `0x15` | No Longer Allowed | | `0x16` | | | `0x17` | | | `0x18` | | | `0x19` | | | `0x1A` | | | `0x1B` | | | `0x1C` | | | `0x1D` | | | `0x1E` | | | `0x1F` | Permission Meta or Info | #### Find, Match, &c This range is broadly intended for finding and matching. Data lookups and order matching are two common use cases. | Code | Description | |-----------------|:-------------------------| | `0x20` | Not Found | | `0x21` | Found | | `0x22` | Match Request Sent | | `0x23` | Awaiting Match | | `0x24` | Match Request Received | | `0x25` | Out of Range | | `0x26` | | | `0x27` | | | `0x28` | | | `0x29` | | | `0x2A` | | | `0x2B` | | | `0x2C` | | | `0x2D` | | | `0x2E` | | | `0x2F` | Matching Meta or Info | #### 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). | Code | Description | |-----------------|:----------------------------| | `0x30` | Other Party Disagreed | | `0x31` | Other Party Agreed | | `0x32` | Sent Offer | | `0x33` | Awaiting Their Ratification | | `0x34` | Awaiting Your Ratification | | `0x35` | Offer Expired | | `0x36` | | | `0x37` | | | `0x38` | | | `0x39` | | | `0x3A` | | | `0x3B` | | | `0x3C` | | | `0x3D` | | | `0x3E` | | | `0x3F` | Negotiation Meta or Info | #### Availability Service or action availability. | Code | Description | |-----------------|:----------------------------| | `0x40` | Unavailable | | `0x41` | Available | | `0x42` | You May Begin | | `0x43` | Not Yet Available | | `0x44` | Awaiting Your Availability | | `0x45` | No Longer Available | | `0x46` | | | `0x47` | | | `0x48` | | | `0x49` | | | `0x4A` | | | `0x4B` | | | `0x4C` | | | `0x4D` | | | `0x4E` | | | `0x4F` | Availability Meta or Info | #### `0x5_` TBD Currently unspecified #### `0x6_` TBD Currently unspecified #### `0x7_` TBD Currently unspecified #### `0x8_` TBD Currently unspecified #### `0x9_` TBD Currently unspecified #### 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 specific meanings for each, as long as they are coherent with the broader definition. | Code | Description | |-----------------|:--------------------------------| | `0xA0` | App-Specific Failure | | `0xA1` | App-Specific Success | | `0xA2` | App-Specific Acceptance / Start | | `0xA3` | App-Specific Awaiting / Before | | `0xA4` | App-Specific Action Required | | `0xA5` | App-Specific Expiry | | `0xA6` | | | `0xA7` | | | `0xA8` | | | `0xA9` | | | `0xAA` | | | `0xAB` | | | `0xAC` | | | `0xAD` | | | `0xAE` | | | `0xAF` | App-Specific Meta or Info | #### `0xB_` TBD Currently unspecified #### `0xC_` TBD Currently unspecified #### `0xD_` TBD Currently unspecified #### Cryptography and Authentication Actions around signatures, cryptography, signing, and application-level authentication. The meta code `0xEF` is often used to signal a payload descibing the algorithm or process used. | Code | Description | |-----------------|:----------------------------| | `0xE0` | Decrypt Failure | | `0xE1` | Decrypt Success | | `0xE2` | Signed | | `0xE3` | Their Signature Required | | `0xE4` | Your Signature Required | | `0xE5` | Auth Expired | | `0xE6` | | | `0xE7` | | | `0xE8` | | | `0xE9` | | | `0xEA` | | | `0xEB` | | | `0xEC` | | | `0xED` | | | `0xEE` | | | `0xEF` | Crypto Info or Meta | #### `0xF0` Off-Chain 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. | Code | Description | |-----------------|:------------------------------| | `0xF0` | Off-Chain Failure | | `0xF1` | Off-Chain Success | | `0xF2` | Off-Chain Process Stared | | `0xF3` | Awaiting Off-Chain Completion | | `0xF4` | Off-Chain Action Required | | `0xF5` | Off-Chain Service Unavailable | | `0xF6` | | | `0xF7` | | | `0xF8` | | | `0xF9` | | | `0xFA` | | | `0xFB` | | | `0xFC` | | | `0xFD` | | | `0xFE` | | | `0xFF` | Off-Chain Info or Meta | ### Example Function Change ```solidity uint256 private startTime; mapping(address => uint) private counters; // Before function increase() public returns (bool _available) { if (now < startTime && counters[msg.sender] == 0) { return false; }; counters[msg.sender] += 1; return true; } // After function increase() public returns (byte _status) { if (now < start) { return hex"43"; } // Not yet available if (counters[msg.sender] == 0) { return hex"10"; } // Not authorized counters[msg.sender] += 1; return hex"01"; // Success } ``` ### Example Sequence Diagrams ``` 0x03 = Waiting 0x31 = Other Party (ie: not you) Agreed 0x41 = Available 0x43 = Not Yet Available Exchange AwesomeCoin DEX TraderBot + + + | | buy(AwesomeCoin) | | | <------------------------+ | buy() | | | <---------------------+ | | | | | Status [0x43] | | +---------------------> | Status [0x43] | | +------------------------> | | | | | | isDoneYet() | | | <------------------------+ | | | | | Status [0x43] | | +------------------------> | | | | | | | | Status [0x41] | | +---------------------> | | | | | | buy() | | | <---------------------+ | | | | | | | | Status [0x31] | | +---------------------> | Status [0x31] | | +------------------------> | | | | | | | | | | | | | + + + ``` ``` 0x01 = Generic Success 0x10 = Disallowed 0x11 = Allowed Token Validation Buyer RegulatedToken TokenValidator IDChecker SpendLimiter + + + + + | buy() | | | | +------------------------> | check() | | | | +-----------------------> | check() | | | | +-----------------------> | | | | | | | | | | Status [0x10] | | | | Status [0x10] | <-----------------------+ | | revert() | <-----------------------+ | | | <------------------------+ | | | | | | | | +---------------------------+ | | | | | | | | | | | Updates ID with provider | | | | | | | | | | | +---------------------------+ | | | | | | | | | | buy() | | | | +------------------------> | check() | | | | +-----------------------> | check() | | | | +-----------------------> | | | | | | | | | | Status [0x11] | | | | | <-----------------------+ | | | | | | | | | | check() | | | +-------------------------------------------> | | | | | | | | | | Status [0x11] | | | Status [0x11] | <-------------------------------------------+ | Status [0x01] | <-----------------------+ | | | <------------------------+ | | | | | | | | | | | | | | | | | | + + + + + ``` ## Rationale ### Encoding ESCs are encoded as a `byte`. Hex values break nicely into high and low nibbles: `category` and `reason`. For instance, `hex"01"` stands for general success and `hex"00"` for general failure. `byte` is quite lightweight, and can be easily packed with multiple codes into a `bytes32` (or similar) if desired. It is also easily interoperable with `uint8`, cast from `enum`s, and so on. #### Alternatives Alternate schemes include `bytes32` and `uint8`. While these work reasonably well, they have drawbacks. `uint8` feels even more similar to HTTP status codes, and enums don't require as much casting. However does not break as evenly as a square table (256 doesn't look as nice in base 10). Packing multiple codes into a single `bytes32` is nice in theory, but poses additional challenges. Unused space may be interpeted as `0x00 Failure`, you can only efficiently pack four codes at once, and there is a challenge in ensuring that code combinations are sensible. Forcing four codes into a packed representation encourages multiple status codes to be returned, which is often more information than strictly nessesary. This can lead to paradoxical results (ex `0x00` and `0x01` together), or greater resorces allocated to interpreting 2564 (4.3 billion) permutations. ### Multiple Returns While there may be cases where packing a byte array of ESCs may make sense, the simplest, most forwards-compatible method of transmission is as the first value of a multiple return. 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. ### Human Readable Developers should not be required to memorize 256 codes. However, they break nicely into a table. Cognitive load is lowered by organizing the table into categories and reasons. `0x10` and `0x11` belong to the same category, and `0x04` shares a reason with `0x24` 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 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. ### EVM Codes The EVM also returns a status code in transactions; specifically `0x00` and `0x01`. This proposal both matches the meanings of those two codes, and could later be used at the EVM level. ### Empty Space Much like how HTTP status codes have large unused ranges, there are totally empty sections in this proposal. The intent is to not impose a complete set of codes up front, and to allow users to suggest uses for these spaces as time progresses. ### Nibble Order Nibble order makes no difference to the machine, and is purely mnemonic. This design was originally in opposite order, but changed it for a few convenience factors. Since it's a different scheme from HTTP, it may feel strange initially, but becomes very natural after a couple hours of use. #### Short Forms Generic is `0x0_`, general codes are consistent with their integer representations ```solidity hex"1" == hex"01" == 1 // with casting ``` #### Contract Categories Many applications will always be part of the same category. For instance, validation will generally be in the `0x10` range. ```solidity contract Whitelist { mapping(address => bool) private whitelist; uint256 private deadline; byte constant private prefix = hex"10"; check(address _, address _user) returns (byte _status) { if (now >= deadline) { return prefix | 5; } if (whitelist[_user]) { return prefix | 1; } return prefix; } } ``` #### Helpers This above also means that working with app-specific enums is slightly easier: ```solidity enum Sleep { Awake, Asleep, REM, FallingAsleep } // From the helper library function appCode(Sleep _state) returns (byte code) { return byte(160 + _state); // 160 = 0xA0 } // Versus function appCode(Sleep _state) returns (byte code) { return byte((16 * _state) + 10); // 10 = 0xA } ``` ## Implementation Reference cases and helper library can be found [here](https://github.com/Finhaven/EthereumStatusCodes) ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).