Broadly applicable status codes for smart contracts.
Abstract
This standard outlines a common set of status codes in a similar vein to 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 on anything other than a clear success (ie: require human intervention), or return a low-context true or false. Status codes are similar-but-orthogonal to reverting with a reason, but aimed at automation, debugging, and end-user feedback (including translation). They are fully compatible with both revert and revert-with-reason.
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, truly exceptional conditions that require halting execution, normal state transitions, and various success cases.
Motivation
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.
Status codes convey a much richer set of information than Booleans, and are able to be reacted to autonomously unlike arbitrary strings.
User Experience (UX)
End users get little to no feedback, and there is no translation layer.
Since ERC1066 status codes are finite and known in advance, we can leverage ERC-1444 to 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.
Status codes convey a much richer set of information than Booleans, and are able to be reacted to autonomously unlike arbitrary strings.
Developer Experience (DX)
Developers currently have very little context exposed by their smart contracts.
At time of writing, other than stepping through EVM execution and inspecting memory dumps directly, it is very difficult to understand what is happening during smart contract execution. By returning more context, developers can write well-decomposed tests and assert certain codes are returned as an expression of where the smart contract got to. This includes status codes as bare values, events, and reverts.
Having a fixed set of codes also makes it possible to write common helper functions to react in common ways to certain signals. This can live off- or on-chain library, lowering the overhead in building smart contracts, and helping raise code quality with trusted shared components.
We also see a desire for this in transactions, and there’s no reason that these status codes couldn’t be used by the EVM itself.
Smart Contract Autonomy
Smart contracts don’t know much about the result of a request beyond pass/fail; they can be smarter with more context.
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.
While clearly related, status codes are complementary to revert-with-reason. Status codes 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.
All of this enables contracts to share a common vocabulary of state transitions, results, and internal changes, without having to deeply understand custom status enums or the internal business logic of collaborator contracts.
Specification
Format
Codes are returned either on their own, or as the first value of a multiple return.
// Status only
functionisInt(uintnum)publicpurereturns(bytestatus){returnhex"01";}// Status and value
uint8privatecounter;functionsafeIncrement(uint8interval)publicreturns(bytestatus,uint8newCounter){uint8updated=counter+interval;if(updated>=counter){counter=updated;return(hex"01",updated);}else{return(hex"00",counter);}}
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 explanatory and layout reasons.
NB: Unspecified codes are not free for arbitrary use, but rather open for further specification.
0x0* Generic
General codes. These double as bare “reasons”, since 0x01 == 1.
Code
Description
0x00
Failure
0x01
Success
0x02
Awaiting Others
0x03
Accepted
0x04
Lower Limit or Insufficient
0x05
Receiver Action Requested
0x06
Upper Limit
0x07
[reserved]
0x08
Duplicate, Unnecessary, or Inapplicable
0x09
[reserved]
0x0A
[reserved]
0x0B
[reserved]
0x0C
[reserved]
0x0D
[reserved]
0x0E
[reserved]
0x0F
Informational or Metadata
0x1* Permission & Control
Also used for common state machine actions (ex. “stoplight” actions).
Code
Description
0x10
Disallowed or Stop
0x11
Allowed or Go
0x12
Awaiting Other’s Permission
0x13
Permission Requested
0x14
Too Open / Insecure
0x15
Needs Your Permission or Request for Continuation
0x16
Revoked or Banned
0x17
[reserved]
0x18
Not Applicable to Current State
0x19
[reserved]
0x1A
[reserved]
0x1B
[reserved]
0x1C
[reserved]
0x1D
[reserved]
0x1E
[reserved]
0x1F
Permission Details or Control Conditions
0x2* Find, Inequalities & Range
This range is broadly intended for finding and matching. Data lookups and order matching are two common use cases.
Code
Description
0x20
Not Found, Unequal, or Out of Range
0x21
Found, Equal or In Range
0x22
Awaiting Match
0x23
Match Request Sent
0x24
Below Range or Underflow
0x25
Request for Match
0x26
Above Range or Overflow
0x27
[reserved]
0x28
Duplicate, Conflict, or Collision
0x29
[reserved]
0x2A
[reserved]
0x2B
[reserved]
0x2C
[reserved]
0x2D
[reserved]
0x2E
[reserved]
0x2F
Matching Meta or Info
0x3* Negotiation & Governance
Negotiation, and very broadly the flow of such transactions. Note that “other party” may be more than one actor (not necessarily the sender).
Code
Description
0x30
Sender Disagrees or Nay
0x31
Sender Agrees or Yea
0x32
Awaiting Ratification
0x33
Offer Sent or Voted
0x34
Quorum Not Reached
0x35
Receiver’s Ratification Requested
0x36
Offer or Vote Limit Reached
0x37
[reserved]
0x38
Already Voted
0x39
[reserved]
0x3A
[reserved]
0x3B
[reserved]
0x3C
[reserved]
0x3D
[reserved]
0x3E
[reserved]
0x3F
Negotiation Rules or Participation Info
0x4* Availability & Time
Service or action availability.
Code
Description
0x40
Unavailable
0x41
Available
0x42
Paused
0x43
Queued
0x44
Not Available Yet
0x45
Awaiting Your Availability
0x46
Expired
0x47
[reserved]
0x48
Already Done
0x49
[reserved]
0x4A
[reserved]
0x4B
[reserved]
0x4C
[reserved]
0x4D
[reserved]
0x4E
[reserved]
0x4F
Availability Rules or Info (ex. time since or until)
0x5* Tokens, Funds & Finance
Special token and financial concepts. Many related concepts are included in other ranges.
Code
Description
0x50
Transfer Failed
0x51
Transfer Successful
0x52
Awaiting Payment From Others
0x53
Hold or Escrow
0x54
Insufficient Funds
0x55
Funds Requested
0x56
Transfer Volume Exceeded
0x57
[reserved]
0x58
Funds Not Required
0x59
[reserved]
0x5A
[reserved]
0x5B
[reserved]
0x5C
[reserved]
0x5D
[reserved]
0x5E
[reserved]
0x5F
Token or Financial Information
0x6* TBD
Currently unspecified. (Full range reserved)
0x7* TBD
Currently unspecified. (Full range reserved)
0x8* TBD
Currently unspecified. (Full range reserved)
0x9* TBD
Currently unspecified. (Full range reserved)
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 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 Awaiting Others
0xA3
App-Specific Acceptance
0xA4
App-Specific Below Condition
0xA5
App-Specific Receiver Action Requested
0xA6
App-Specific Expiry or Limit
0xA7
[reserved]
0xA8
App-Specific Inapplicable Condition
0xA9
[reserved]
0xAA
[reserved]
0xAB
[reserved]
0xAC
[reserved]
0xAD
[reserved]
0xAE
[reserved]
0xAF
App-Specific Meta or Info
0xB* TBD
Currently unspecified. (Full range reserved)
0xC* TBD
Currently unspecified. (Full range reserved)
0xD* TBD
Currently unspecified. (Full range reserved)
0xE* Encryption, Identity & Proofs
Actions around signatures, cryptography, signing, and application-level authentication.
The meta code 0xEF is often used to signal a payload describing the algorithm or process used.
Code
Description
0xE0
Decrypt Failure
0xE1
Decrypt Success
0xE2
Awaiting Other Signatures or Keys
0xE3
Signed
0xE4
Unsigned or Untrusted
0xE5
Signature Required
0xE6
Known to be Compromised
0xE7
[reserved]
0xE8
Already Signed or Not Encrypted
0xE9
[reserved]
0xEA
[reserved]
0xEB
[reserved]
0xEC
[reserved]
0xED
[reserved]
0xEE
[reserved]
0xEF
Cryptography, ID, or Proof Metadata
0xF* 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
Awaiting Off-Chain Process
0xF3
Off-Chain Process Started
0xF4
Off-Chain Service Unreachable
0xF5
Off-Chain Action Required
0xF6
Off-Chain Expiry or Limit Reached
0xF7
[reserved]
0xF8
Duplicate Off-Chain Request
0xF9
[reserved]
0xFA
[reserved]
0xFB
[reserved]
0xFC
[reserved]
0xFD
[reserved]
0xFE
[reserved]
0xFF
Off-Chain Info or Meta
As a Grid
Â
0x0* General
0x1* Permission & Control
0x2* Find, Inequalities & Range
0x3* Negotiation & Governance
0x4* Availability & Time
0x5* Tokens, Funds & Finance
0x6* TBD
0x7* TBD
0x8* TBD
0x9* TBD
0xA* Application-Specific Codes
0xB* TBD
0xC* TBD
0xD* TBD
0xE* Encryption, Identity & Proofs
0xF* Off-Chain
0x*0
0x00 Failure
0x10 Disallowed or Stop
0x20 Not Found, Unequal, or Out of Range
0x30 Sender Disagrees or Nay
0x40 Unavailable
0x50 Transfer Failed
0x60 [reserved]
0x70 [reserved]
0x80 [reserved]
0x90 [reserved]
0xA0 App-Specific Failure
0xB0 [reserved]
0xC0 [reserved]
0xD0 [reserved]
0xE0 Decrypt Failure
0xF0 Off-Chain Failure
0x*1
0x01 Success
0x11 Allowed or Go
0x21 Found, Equal or In Range
0x31 Sender Agrees or Yea
0x41 Available
0x51 Transfer Successful
0x61 [reserved]
0x71 [reserved]
0x81 [reserved]
0x91 [reserved]
0xA1 App-Specific Success
0xB1 [reserved]
0xC1 [reserved]
0xD1 [reserved]
0xE1 Decrypt Success
0xF1 Off-Chain Success
0x*2
0x02 Awaiting Others
0x12 Awaiting Other’s Permission
0x22 Awaiting Match
0x32 Awaiting Ratification
0x42 Paused
0x52 Awaiting Payment From Others
0x62 [reserved]
0x72 [reserved]
0x82 [reserved]
0x92 [reserved]
0xA2 App-Specific Awaiting Others
0xB2 [reserved]
0xC2 [reserved]
0xD2 [reserved]
0xE2 Awaiting Other Signatures or Keys
0xF2 Awaiting Off-Chain Process
0x*3
0x03 Accepted
0x13 Permission Requested
0x23 Match Request Sent
0x33 Offer Sent or Voted
0x43 Queued
0x53 Hold or Escrow
0x63 [reserved]
0x73 [reserved]
0x83 [reserved]
0x93 [reserved]
0xA3 App-Specific Acceptance
0xB3 [reserved]
0xC3 [reserved]
0xD3 [reserved]
0xE3 Signed
0xF3 Off-Chain Process Started
0x*4
0x04 Lower Limit or Insufficient
0x14 Too Open / Insecure
0x24 Below Range or Underflow
0x34 Quorum Not Reached
0x44 Not Available Yet
0x54 Insufficient Funds
0x64 [reserved]
0x74 [reserved]
0x84 [reserved]
0x94 [reserved]
0xA4 App-Specific Below Condition
0xB4 [reserved]
0xC4 [reserved]
0xD4 [reserved]
0xE4 Unsigned or Untrusted
0xF4 Off-Chain Service Unreachable
0x*5
0x05 Receiver Action Required
0x15 Needs Your Permission or Request for Continuation
0x25 Request for Match
0x35 Receiver’s Ratification Requested
0x45 Awaiting Your Availability
0x55 Funds Requested
0x65 [reserved]
0x75 [reserved]
0x85 [reserved]
0x95 [reserved]
0xA5 App-Specific Receiver Action Requested
0xB5 [reserved]
0xC5 [reserved]
0xD5 [reserved]
0xE5 Signature Required
0xF5 Off-Chain Action Required
0x*6
0x06 Upper Limit
0x16 Revoked or Banned
0x26 Above Range or Overflow
0x36 Offer or Vote Limit Reached
0x46 Expired
0x56 Transfer Volume Exceeded
0x66 [reserved]
0x76 [reserved]
0x86 [reserved]
0x96 [reserved]
0xA6 App-Specific Expiry or Limit
0xB6 [reserved]
0xC6 [reserved]
0xD6 [reserved]
0xE6 Known to be Compromised
0xF6 Off-Chain Expiry or Limit Reached
0x*7
0x07 [reserved]
0x17 [reserved]
0x27 [reserved]
0x37 [reserved]
0x47 [reserved]
0x57 [reserved]
0x67 [reserved]
0x77 [reserved]
0x87 [reserved]
0x97 [reserved]
0xA7 [reserved]
0xB7 [reserved]
0xC7 [reserved]
0xD7 [reserved]
0xE7 [reserved]
0xF7 [reserved]
0x*8
0x08 Duplicate, Unnecessary, or Inapplicable
0x18 Not Applicable to Current State
0x28 Duplicate, Conflict, or Collision
0x38 Already Voted
0x48 Already Done
0x58 Funds Not Required
0x68 [reserved]
0x78 [reserved]
0x88 [reserved]
0x98 [reserved]
0xA8 App-Specific Inapplicable Condition
0xB8 [reserved]
0xC8 [reserved]
0xD8 [reserved]
0xE8 Already Signed or Not Encrypted
0xF8 Duplicate Off-Chain Request
0x*9
0x09 [reserved]
0x19 [reserved]
0x29 [reserved]
0x39 [reserved]
0x49 [reserved]
0x59 [reserved]
0x69 [reserved]
0x79 [reserved]
0x89 [reserved]
0x99 [reserved]
0xA9 [reserved]
0xB9 [reserved]
0xC9 [reserved]
0xD9 [reserved]
0xE9 [reserved]
0xF9 [reserved]
0x*A
0x0A [reserved]
0x1A [reserved]
0x2A [reserved]
0x3A [reserved]
0x4A [reserved]
0x5A [reserved]
0x6A [reserved]
0x7A [reserved]
0x8A [reserved]
0x9A [reserved]
0xAA [reserved]
0xBA [reserved]
0xCA [reserved]
0xDA [reserved]
0xEA [reserved]
0xFA [reserved]
0x*B
0x0B [reserved]
0x1B [reserved]
0x2B [reserved]
0x3B [reserved]
0x4B [reserved]
0x5B [reserved]
0x6B [reserved]
0x7B [reserved]
0x8B [reserved]
0x9B [reserved]
0xAB [reserved]
0xBB [reserved]
0xCB [reserved]
0xDB [reserved]
0xEB [reserved]
0xFB [reserved]
0x*C
0x0C [reserved]
0x1C [reserved]
0x2C [reserved]
0x3C [reserved]
0x4C [reserved]
0x5C [reserved]
0x6C [reserved]
0x7C [reserved]
0x8C [reserved]
0x9C [reserved]
0xAC [reserved]
0xBC [reserved]
0xCC [reserved]
0xDC [reserved]
0xEC [reserved]
0xFC [reserved]
0x*D
0x0D [reserved]
0x1D [reserved]
0x2D [reserved]
0x3D [reserved]
0x4D [reserved]
0x5D [reserved]
0x6D [reserved]
0x7D [reserved]
0x8D [reserved]
0x9D [reserved]
0xAD [reserved]
0xBD [reserved]
0xCD [reserved]
0xDD [reserved]
0xED [reserved]
0xFD [reserved]
0x*E
0x0E [reserved]
0x1E [reserved]
0x2E [reserved]
0x3E [reserved]
0x4E [reserved]
0x5E [reserved]
0x6E [reserved]
0x7E [reserved]
0x8E [reserved]
0x9E [reserved]
0xAE [reserved]
0xBE [reserved]
0xCE [reserved]
0xDE [reserved]
0xEE [reserved]
0xFE [reserved]
0x*F
0x0F Informational or Metadata
0x1F Permission Details or Control Conditions
0x2F Matching Meta or Info
0x3F Negotiation Rules or Participation Info
0x4F Availability Rules or Info (ex. time since or until)
0x5F Token or Financial Information
0x6F [reserved]
0x7F [reserved]
0x8F [reserved]
0x9F [reserved]
0xAF App-Specific Meta or Info
0xBF [reserved]
0xCF [reserved]
0xDF [reserved]
0xEF Cryptography, ID, or Proof Metadata
0xFF Off-Chain Info or Meta
Example Function Change
uint256privatestartTime;mapping(address=>uint)privatecounters;// Before
functionincrease()publicreturns(bool_available){if(now<startTime&&counters[msg.sender]==0){returnfalse;};counters[msg.sender]+=1;returntrue;}// After
functionincrease()publicreturns(byte_status){if(now<start){returnhex"44";}// Not yet available
if(counters[msg.sender]==0){returnhex"10";}// Not authorized
counters[msg.sender]+=1;returnhex"01";// Success
}
Status codes are encoded as a byte. Hex values break nicely into high and low nibbles: category and reason. For instance, 0x01 stands for general success (ie: true) and 0x00 for general failure (ie: false).
As a general approach, all even numbers are blocking conditions (where the receiver does not have control), and odd numbers are nonblocking (the receiver is free to continue as they wish). This aligns both a simple bit check with the common encoding of Booleans.
bytes1 is very lightweight, portable, easily interoperable with uint8, cast from enums, 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 interpreted 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 necessarily. This can lead to paradoxical results (ex 0x00 and 0x01 together), or greater resources allocated to interpreting 2564 (4.3 billion) permutations.
Multiple Returns
While there may be cases where packing a byte array of status codes 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 tuples.
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. Status code 0x10 is just as comfortable as HTTP 401, for example.
Localizations
One commonly requested application of this spec is human-readable translations of codes. This has been moved to its own proposal: ERC-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.
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.
Beyond Errors
This spec is intended to be much more than a set of common errors. One design goal is to enable easier contract-to-contract communication, protocols built on top of status codes, and flows that cross off-chain. Many of these cases include either expected kinds of exception state (as opposed to true errors), neutral states, time logic, and various successes.
Just like how HTTP 200 has a different meaning from HTTP 201, ERC-1066 status codes can relay information between contract beyond simply pass or fail. They can be thought of as the edges in a graph that has smart contracts as nodes.
Fully revertable
This spec is fully compatible with revert-with-reason and does not intend to supplant it in any way. Both by reverting with a common code, the developer can determine what went wrong from a set of known error states.
Further, by leveraging ERC-1066 and a translation table (such as in ERC-1444) in conjunction, developers and end users alike can receive fully automated human-readable error messages in the language and phrasing of their choice.
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
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.