# ErrorExplainer: Automated Extraction of Error Contexts from Smart Contracts
## 5 Source Code Analysis
### Intra-Component Analysis
#### 5.2.3 Lifting Error Contexts  
The lifting phase has two responsibilities: (1) apply lifting rules for each transaction-reverting statement of require, assert, revert, and throw; and (2) collect custom error definitions and branch predicates needed to complete the normalized errors. **Custom Error Definitions. ** A custom error is declared with the error keyword. From the source code, ErrorExplainer extracts these declarations and parses them into a pair of name (\(n\)) and argument type list (\(\hat{a}\)). The resulting map is stored in \(\mathbb{D}_{c}\) and later queried during lifting to encode custom errors (Section 5.2.1). **Error Conditions. ** For every transaction-reverting statement, ErrorExplainer identifies the predicate that triggers the error. Conditional constructs, such as require and assert, include their own predicates. However, unconditional constructs, such as revert and throw, do not have predicates. In such cases, the effective guard condition is determined from the preceding context. Specifically, the immediately preceding control-flow condition becomes the effective condition for these unconditional error statements. ErrorExplainer therefore captures predicates from conditional statements (i.e., \(\mathtt{if}\)) and the conditional ternary operator (i.e., \(a?b:c\)) and stores a reference to the condition in \(\mathbb{B}\). Whenever a new conditional statement or operator is encountered, \(\mathbb{B}\) is updated. In this way, ErrorExplainer can keep track of the branch predicates and uses them as the error conditions for the unconditional error statements. Lifting Rules. Traversing the statements in the component, ErrorExplainer applies the rules in Table 4 to lift normalized errors from the transaction-reverting statements, require, assert, revert, and throw. 1. require (Rule 1a, 1b, 1c): **Rule 1a. ** The require statement includes the error condition. If the error output is a literal string (e.g., \"outString\"), ErrorExplainer encodes it via an encoding function for strings, \(\mathsf{Str}()\), as \(\mathsf{Str}(\textit{``outString''})\). For example, \[\ell:\texttt{require}(x>0,\text{``Value must be positive''})\] is lifted to a normalized error \[\{\tau:\texttt{string}, \bar{\epsilon}:\texttt{0x0x0x379a}\cdots 16566616c\cdots 0000,\] \[\epsilon:\text{``Value must be positive'', }\phi:x>0,\ r:\ell\},\] where the line number \(\ell\) identifies the statement in the source code. (For brevity, we omit the description of \(r\) in other lifting rules). **Rule 1b. ** If the second argument is a custom error \(n(args)\), then ErrorExplainer sets the type of the error message as \(\texttt{custom}(n)\) and encodes it by \(\texttt{Custom}(n,args)\), which retrieves the full signature of the custom error from \(\mathbb{D}_{\epsilon}(n)\) and returns the first four bytes that correspond to the custom error selector only. **Rule 1c. ** If the error output is empty or omitted, then the three fields, \(\tau\), \(\bar{\epsilon}\), and \(\epsilon\) are represented as \(\bot\). 2. assert (Rule 2) An assert statement acts as a predefined custom error of Panic(0x01) without explicit error messages. Thus, it is lifted as a custom error with \(\texttt{Custom}(\texttt{Panic},0x01)\) and the given error condition \(\phi\). 3. revert (Rule 3a, 3b, 3c) revert follows the same sub-rules as require for \(\tau\), \(\bar{\epsilon}\), and \(\epsilon\), but it lacks an explicit predicate. Thus, the branch predicate in \(\mathbb{B}\) is used for \(\phi\). Additionally, if revert is used without any arguments, it is lifted as the same as throw (Rule 3c). 4. throw (Rule 4) Legacy throw statements carry neither the error message nor the error condition. Thus, we lift it into an empty-output normalized error that inherits the error condition from \(\mathbb{B}\).