ConceptioArchivearXiv CS
arXiv CSopen access

ParityFuzz: Finding Inconsistencies across Solidity Compilers via Fine-Grained Mutation and Differential Analysis

2026 · arxiv_cs
arXiv CS · Papers · License: Open Access · 2026
Open Source ↗Direct PDF ↓
software-architecturesoftware-engineeringtesting
software engineering, software architecture, testing

ParityFuzz: Finding Inconsistencies across Solidity Compilers via Fine-Grained Mutation and Differential Analysis Bowei Sua , Mingxi Yea,∗ , Yuhong Nana , Peilin Zhenga and Zibin Zhenga

arXiv:2605.09051v1 [cs.SE] 9 May 2026

a School of Software Engineering, Sun Yat-sen University, Zhuhai, 519082, China

ARTICLE INFO

ABSTRACT

Keywords: Solidity Compiler Blockchain Smart Contract Fuzzing

The Solidity smart contract ecosystem has rapidly grown, leading to multiple compilers targeting different blockchain platforms or offering improved compilation efficiency. Although many compilers aim to be compatible with the primary Solidity compiler (Solc), significant inconsistencies in compilation and execution remain. These inconsistencies hinder contract migration, mislead developers during debugging, and may introduce exploitable vulnerabilities, causing potential financial losses. Existing testing techniques mainly focus on bugs within a single compiler or perform differential testing across compilers targeting the same environment. However, these approaches are inadequate for detecting inconsistencies across Solidity compilers, as they lack mechanisms to explore inconsistencytriggering conditions and do not support comparing bytecode generated for different environments. To address this gap, we propose ParityFuzz, a cross-compiler differential testing framework for Solidity. ParityFuzz operates in three stages. First, it generates a rich set of mutation rules (i.e., syntax-oriented and boundary-oriented mutation rules) by analyzing source code (i.e., compiler and execution environment). Second, it employs a reinforcement learning-based strategy to select the most promising rules for mutating test programs. Finally, it detects inconsistencies by compiling and executing these programs on multiple compilers, then normalizing and comparing their results. Our evaluation demonstrates that ParityFuzz is both efficient and effective. It improves test program generation, achieving up to an 18× higher compilation success rate and 1.8× greater code coverage compared to state-of-the-art fuzzers. In total, ParityFuzz has uncovered 64 previously unknown inconsistencies across six popular compilers. Notably, our findings have led to 11 fixes by developers and received a bounty from the Polkadot community.

1. Introduction The Solidity smart contract ecosystem has flourished in recent years, leading to the emergence of multiple Solidity compilers. There are two main reasons for this trend. First, some blockchain platforms aim to attract developers by supporting Solidity. Since these platforms do not natively support the Ethereum Virtual Machine (EVM), they develop new compilers that enable the deployment of Solidity smart contracts. Second, additional compilers have been developed to improve the performance and optimization of Solidity compilers, even on platforms that already support EVM. Several popular Solidity compilers, including Solc [5], Revive [44], Zksolc [20], Solang [34], Sold [18], and Solar [26], support blockchain ecosystems that collectively hold more than $100 billion in Total Value Locked (TVL), according to DefiLlama [9]. This highlights the critical role that Solidity compilers play in today’s blockchain infrastructure. However, the increasing number of Solidity compilers has also introduced significant inconsistencies among them. These inconsistencies can have serious real-world consequences. For example, inconsistent compilation behaviors may hinder the migration of smart contracts across different blockchain platforms, potentially causing deployment failures. They may also mislead developers during contract development and debugging, since identical Solidity programs ∗ Corresponding author

[email protected] (B. Su); [email protected] (M. Ye); [email protected] (Y. Nan); [email protected] (P. Zheng); [email protected] (Z. Zheng) ORCID (s):

Bowei Su et al.: Preprint submitted to Elsevier

may produce different compilation results. In addition, such inconsistencies may introduce vulnerabilities that adversaries could exploit, potentially resulting in substantial financial losses. Therefore, systematic detection of these inconsistencies is critical for trustworthy smart contracts. Challenges. However, systematically detecting cross-compiler inconsistencies in Solidity is far from straightforward. First, generating programs that trigger inconsistencies is difficult. Existing fuzzers rely on coarse-grained mutation strategies that struggle to produce syntactically diverse programs. POLYGLOT [3] manipulates the program’s IR, Fuzzol [22] modifies AST nodes, and afl-compiler-fuzzer [14] disassembles and reassembles programs, often requiring numerous attempts to generate complex structures. Moreover, these mutations generally lack boundary-awareness, failing to target conditions that reveal subtle inconsistencies. Second, identifying inconsistencies across compilers is nontrivial. Existing tools either focus on detecting bugs in a single compiler [3, 14, 22] or perform differential testing across multiple compilers that target the same execution environment and platform [45, 46, 4]. However, Solidity compilers often target different execution environments, producing outputs that cannot be directly compared, which makes these existing approaches unsuitable for crosscompiler inconsistency detection. Our Insight. While Solidity compilers produce platformspecific bytecode, their interpretation of the language syntax should be consistent. We observe that compilers often diverge when handling corner cases involving numerous boundary Page 1 of 15

Short Title of the Article

conditions (see Section 2 for details). These differing approaches are a primary source of cross-compiler inconsistencies, the main targets for detection in this paper. Such inconsistencies manifest as differences where, for the same source code, one compiler might crash while another does not, one produces runnable bytecode while the other’s output fails, or their resulting bytecodes execute with different outcomes. Our Approach. We propose ParityFuzz, a novel framework for detecting cross-compiler inconsistencies in Solidity. The architecture of ParityFuzz consists of three main components, namely a mutation rule generator, a fine-grained mutator, and an execution-based detector. The fuzzing process begins with the mutation rule generator, which creates a rich set of mutation rules. The fine-grained mutator then adaptively applies these rules to seed programs to generate variants likely to expose compiler inconsistencies. Finally, the execution-based detector identifies inconsistencies by compiling and running these variants across different compilers, analyzing not only errors but also subtle differences in their execution outcomes. Our evaluation shows that ParityFuzz outperforms state-of-the-art fuzzers (i.e., POLYGLOT [3], afl-compilerfuzzer [14], FUZZOL [22], and solc_ossfuzz [4]) in both efficiency and efficacy. Specifically, ParityFuzz achieves up to an 18× higher compilation success rate and 1.8× greater code coverage, demonstrating its ability to generate high-quality test programs. In terms of efficacy, ParityFuzz detected 31× more inconsistencies than the compared fuzzers. We applied ParityFuzz to find inconsistencies among six popular Solidity compilers (i.e., Solc [5], Revive [44], Zksolc [20], Solang [34], Sold [18], and Solar [26]). As a result, 27 inconsistencies have been confirmed as bugs by the respective developers, and 11 of them have already been fixed. In summary, this paper makes the following contributions: • We present ParityFuzz, the first fuzzing framework designed specifically to detect inconsistencies across Solidity compilers. • We design a novel fuzzing strategies that combines a mutation rule generator with a fine-grained mutator to efficiently trigger cross-compiler inconsistencies. • We implement an execution-based oracle, which identifies not only compilation and runtime errors but also subtle differences in execution outcomes. • We demonstrate the effectiveness of ParityFuzz through a comprehensive evaluation. ParityFuzz outperforms state-of-the-art tools and has identified 64 previously unknown inconsistencies across six popular Solidity compilers. The rest of this paper is organized as follows. Section 2 provides background information and motivating examples. Section 3 and Section 4 describe the design and methodology

Bowei Su et al.: Preprint submitted to Elsevier

Solc YUL

Solidity

EVM Bytecode

Revive

RISC-V Bytecode

Zksolc

EVM Bytecode

Solang

BPF Bytecode

Sold

TVM Bytecode

EVM

PolkaVM ZKEVM SVM TVM

Solar

None

Compiler

Virtual Machine

Figure 1: The compilation and execution process under different Solidity compilers.

of ParityFuzz, followed by implementation details in Section 5. Section 6 presents a comprehensive evaluation. Section 7 discusses threats to validity and limitations. Section 8 reviews related work, and Section 9 concludes the paper.

2. Background and Motivation 2.1. Solidity Compilers Figure 1 illustrates six widely used Solidity compilers. A Solidity compiler primarily translates source code into bytecode that can be executed on a virtual machine. In practice, this bytecode executes within a complex execution environment, which we refer to as the executor. In our work, the executor encompasses not only the VM itself, but also higher-level runtime modules such as transaction handling, contract calls, and state management. Our analysis evaluates the behavior of programs across this entire execution flow. Inconsistencies across Solidity compilers can arise from two main sources. First, some compilers (e.g., Solc, Zksolc, Solang) target different blockchain platforms. To support each platform, they require customized designs that produce the bytecode needed for that environment. Second, even compilers targeting the same environment can adopt different internal designs. For instance, Solar implements aggressive optimizations to improve compilation efficiency, while its backend remains under active development. These factors can lead to observable differences in compilation and execution outputs, which ParityFuzz detects as inconsistencies.

2.2. Cross-Compiler Inconsistencies To systematically study these inconsistencies, we first analyzed existing compiler behaviors. Based on these observations, we propose a taxonomy that classifies cross-compiler inconsistencies into four types. • Error Message Inconsistency (EMI). Both compilers fail to compile the same code, but one provides a detailed error message (e.g., locations and causes) while the other does not. Vague error details from a compiler can significantly hinder debugging and degrade the developer experience. Page 2 of 15

Short Title of the Article

• Compilation Status Inconsistency (CSI). One compiler successfully compiles the code, while another reports an error. This type of inconsistency can prevent a program that is valid in one blockchain ecosystem from being deployed in another. • Execution Status Inconsistency (ESI). Bytecode from one compiler executes successfully, while bytecode from another fails at runtime. This can occur either when a compiler generates invalid bytecode for its target executor, or when the executor itself imposes inappropriate restrictions. Such failures often cause transactions to revert, impeding the migration of decentralized applications. • Execution Output Inconsistency (EOI). Bytecode from both compilers executes without error but produces different results. This semantic inconsistency can subvert the program’s intended logic and may lead to significant financial losses.

2.3. Motivating Example Figure 2 illustrates a case of compilation status inconsistency, where Solc accepts the program while Solang reports a compilation error. This compilation error originates in the storage_align of Solang. As shown in Figure 3, during the recursive calculation of storage alignment for structure fields, the function encounters a user-defined value type. Because the Solang compiler lacks a specific implementation for this case, it hits an unimplemented!() branch and fails. Overall, the compilation error is due to Solang’s incomplete support for user-defined types within structures. This motivating example shows that inconsistencies often arise from differing boundary conditions across compilers and executors, such as the unimplemented path in Figure 3. Therefore, generating programs that trigger these boundary conditions can help uncover inconsistencies between compilers. Beyond this, some inconsistencies do not directly trigger boundary conditions but still activate the surrounding code blocks. These inconsistencies arise from design differences among compilers and executors, and their exact causes are often difficult to trace in the source code. As a result, they typically require programs with diverse syntactic structures to be triggered. Since they can still reach code blocks containing boundary conditions, random mutation strategies targeting these code blocks can be employed to expose such inconsistencies. Finally, we can conclude that triggering inconsistencies requires both the generation of syntactically diverse programs and the presence of boundary conditions in the source code.

2.4. Limitations of State-of-the-art Fuzzers Existing approaches [3, 14, 22, 4] are primarily designed to detect bugs within individual Solidity compilers. When it comes to uncovering inconsistencies across different compilers, these approaches face two limitations. Difficulty in Generating Inconsistency-Triggering Programs. First, the mutation strategies in existing fuzzers Bowei Su et al.: Preprint submitted to Elsevier

type MyValueType is uint ; contract X { 3 struct S { MyValueType x ; } 4} 1 2

Figure 2: An example of compilation status inconsistency. Solc compiles the program successfully, whereas Solang reports a compilation error. pub fn storage_align (& self , ns : & Namespace ) -> BigInt { 2 let length = match self { 3 // ... 4 Type :: Array ( ty , _ ) = > { 5 // ... 6 ty . storage_align ( ns ) 7 // ... 8 } 9 Type :: Struct ( s ) = > s 10 . definition ( ns ) 11 . fields 12 . iter () 13 . filter (| f | ! f . infinite_size ) 14 . map (| f | f . ty . storage_align ( ns )) 15 . max () 16 . unwrap_or_else (|| 1. into () ) , 17 Type :: String | Type :: DynamicBytes => // ... 18 Type :: InternalFunction { .. } = > // ... 19 // ... 20 _ = > unimplemented !() , 21 // ... 22 }; 23 // ... 24 } 1

Figure 3: The source code of the Solang compiler that handles struct

[3, 22, 14] are relatively coarse-grained, making it difficult to generate syntactically diverse programs. Specifically, POLYGLOT[3] inserts, deletes, and replaces elements in the program’s intermediate representation (IR). Fuzzol[22] modifies the nodes of the program’s AST. afl-compilerfuzzer[14] disassembles programs and then reassembles them. These coarse mutation strategies rely on a large number of failed attempts before they can mutate relatively complex syntactic structures. Second, the mutation strategies lack boundaryorientation. As shown in Figure 3, boundary conditions serve as an intuitive manifestation of certain inconsistencies. The mutation strategies of fuzzers [3, 14, 22] are relatively generic, making it difficult to generate programs that trigger boundary conditions. Apart from this, LLM4CBI [47] incorporates 13 mutation rules (e.g., inserting an if statement Page 3 of 15

Short Title of the Article Source Code

Execution-Based Detection

Prompts For Rule Selection

Prompts For Rule Generation Prompts For Rule Mutation

Compiler0

Executor0

Compiler1

Executor1

Compiler2

Executor2

Detection Reports

Detector Reward calculator

Mutation Rules Seed Programs

Finetuning Dataset

New Program Fine-tune

Rule Generation

Fine-Grained Mutation

Figure 4: Workflow of ParityFuzz.

or a loop) for compiler bug isolation. DFUZZ [54] extracts edge cases from DL library APIs to guide program mutation. These fuzzers’ mutation strategies focus on domain-specific problems rather than compiler inconsistencies, and therefore also lack boundary-orientation. Difficulty in Identifying Inconsistencies. Tools [3, 14, 22] only support the detection of compilation error bugs. Therefore, these tools [3, 14, 22] can only detect inconsistencies in the compilation results. DeSCDT [45] and yul_proto_diff_ossfuzz[4] aim to detect optimization issues in the Solc compiler. They compare the execution results of programs compiled with and without optimizations to check for any differences. Therefore, these tools can only detect whether the program executes correctly in the EVM and cannot be extended to executors matching other compilers.

3. Overview of ParityFuzz 3.1. Our idea This work presents ParityFuzz, a fuzzer designed to automatically generate Solidity programs that reveal inconsistencies among Solidity compilers. Effective test programs exhibit two key properties, including syntactic diversity and a focus on boundary conditions. Our approach combines two types of mutation rules to effectively generate test programs. Given boundary conditions in source code (i.e., compiler and executor), ParityFuzz generates mutation rules based on syntactic features from the source code in order to achieve syntactic diversity. ParityFuzz also generates mutation rules based on boundary conditions in order to trigger inconsistencies. The extraction is based on a Large Language Model (LLM) by analyzing source code. To adaptively select mutation rules for fuzzing, ParityFuzz first classifies all mutation rules and applies a recurring mutation loop to keep generating more promising test programs for triggering cross-compiler inconsistencies. This process in continuously optimized using reinforcement learning to improve its effectiveness over time.

Bowei Su et al.: Preprint submitted to Elsevier

Finally, ParityFuzz detects inconsistencies by executing each generated program across multiple compilers and comparing the outcomes. To ensure an accurate comparison, a normalization step decodes and aligns the outputs from different executors, accounting for variations in their semantics.

3.2. Workflow of ParityFuzz As illustrated in Figure 4, the ParityFuzz framework operates in three main stages: mutation rule generation, finegrained mutation, and execution-based detection. The mutation rule generation stage begins by identifying boundary conditions within the target compilers and executors. ParityFuzz then analyzes the syntactic features that activate these conditions to automatically generate syntaxoriented (SO) mutation rules. These rules are subsequently refined into boundary-oriented (BO) rules designed to specifically trigger sensitive areas. In the fine-grained mutation stage, ParityFuzz adaptively mutates seed programs. It employs a mutation-based fuzzing loop to keep generating more promising test programs from a given seed program. This process is continuously optimized via reinforcement learning, which uses feedback from the compilation and execution of new variants to improve future choices. The final execution-based detection stage identifies inconsistencies through differential analysis. Newly generated programs are compiled and run across multiple toolchains. We apply semantic alignment and compare testing output. After filtering out false positives, programs that trigger inconsistencies are documented and reported.

3.3. Fuzzing Scope We identify the following six popular Solidity compilers as the subjects of our study: Solc, Revive, Zksolc, Solang, Sold, and Solar. According to DefiLlama [9], the Total Value Locked (TVL) of these compilers reaches 100 billion dollars as of August 2025. These compilers represent blockchain platforms with one of the highest Total Value Locked (TVL) records, making the security of these compilers significant. Among them, although the Solar compiler does not yet have a backend, it is planned to support EVM in the future [8]. Page 4 of 15

Short Title of the Article Source Code

Table 1 Inconsistency detection supported by ParityFuzz. Compiler Inconsistency Error Message Compilation Status Execution Status Execution Output

Solc

Revive

Zksolc

Solang

Sold

Solar

✓ ✓ ✓ ✓

✓ ✓ ✓ ✓

✓ ✓ ✓ ✓

✓ ✓ ✓ ✓

✓ ✓ -

✓ ✓ -

Building upon this selection, our research focuses on identifying four types of inconsistencies (i.e., EMI, CSI, ESI, EOI) across the six Solidity compilers. Table 1 summarizes the types of inconsistency detection supported by ParityFuzz for each compiler. Notably, the program execution environment is missing for Sold, and Solar currently does not support bytecode generation. Thus, neither compiler can detect inconsistencies related to execution results.

4. Details of ParityFuzz 4.1. Mutation Rule Generation Two sets of mutation rules are generated: syntax-oriented rules and boundary-oriented rules. As for boundary-oriented rules, ParityFuzz extracts boundary conditions from the source code of the compiler and executor and uses boundary conditions to generate rules. There are two main challenges in generating boundaryoriented mutation rules. (1) mutation rules need to be clear. The most straightforward approach is to let the LLM directly analyze the source code containing boundary conditions and generate mutation rules. However, such naively generated mutation rules are often vague, such as "inserting unsupported types". Due to the wide variety of Solidity types, the LLM struggles to determine the specific type for mutation rules. DFUZZ[54] addresses this by listing all possible types, allowing the LLM to choose the appropriate one for edge cases. However, compiler boundary conditions are more complex and lack a fixed format, making it difficult for the LLM to select the correct type. Additionally, mutation rule ambiguity can arise from vague descriptions, like "increase the complexity of nested types". DFUZZ can only complete specific types but struggles to handle vague concepts like "complexity". (2) mutation rules need to be comprehensive. A piece of source code may contain multiple boundary conditions, and each boundary condition can have various triggering methods. Mutation rules generated by LLMs through direct analysis of the code often miss many ways to trigger these boundary conditions. To address the above challenges, ParityFuzz adopts a stepwise approach to enhancing mutation rules, shown in Figure 5. First, once the code blocks containing boundary conditions are obtained, ParityFuzz identifies the program features that can trigger these code blocks and generate simple mutation rules for each feature (syntax-oriented rules). Building on these features and syntax-oriented rules, ParityFuzz then creates more complex mutation rules (boundary-oriented rules). Bowei Su et al.: Preprint submitted to Elsevier

Prompt

Prompt

Prompt

Prompt

Boundary Conditions

Program Feature

SO Rules

BO Rules

Boundary Condition Extraction

Program Feature Identification

Syntax-Oriented Rule Generation

Boundary-Oriented Rule Generation

Figure 5: The process of rule generation.

To ensure these rules are effectively applied, ParityFuzz handles two key issues. Specifically, to address the problem of vague rules in Challenge (1), ParityFuzz embeds syntaxoriented rules into the prompt for boundary-oriented rule generation, enabling the LLM to either select one as a new rule or generate a new one based on it. Since syntaxoriented rules are inherently simple and clear, the generated rules also retain clarity. To address the problem of missing rules in Challenge (2), ParityFuzz embeds one boundary condition and one feature into the prompt at a time, ensuring that the generated rule targets the feature and triggers the corresponding boundary condition. Boundary Condition Extraction. This step aims to extract boundary conditions present in the source code. Boundary conditions can be explicit or implicit. Explicit boundary conditions include Exception and Error Handling, Unimplemented Features, Code Path Validity Checks, and Assertions. These usually have fixed identifiers such as "error", "panic", "unimplemented", "unreachable", or "assert". For example, Figure 3 illustrates a code block containing the identifier "unimplemented". Therefore, we can manually extract code blocks containing these identifiers. Implicit boundary conditions, such as type casting and memory out-of-bounds, are harder to identify manually. For these, we use LLM to assist extraction. Finally, we obtain code blocks containing boundary conditions. Program Feature Identification. This step aims to summarize the language features that can trigger code blocks containing boundary conditions. As shown in Figure 3, the compiler source code reveals that boundary conditions are typically triggered through complex control flows. This reliance makes it difficult for the LLM to directly generate programs that can reach such conditions. Therefore, we first summarize the program features associated with code blocks containing boundary conditions. Building on these features, the LLM can generate precise mutation rules to trigger boundary conditions. We directly employ the LLM to analyze the code blocks containing boundary conditions and identify the involved Solidity language features. For example, in Figure 3, the

Page 5 of 15

Short Title of the Article Solidity language feature: {feature} Generate mutation rules for fuzzing. 1. List all mutation points in the {feature} {feature}; each point may have multiple mutation rules. 2. Mutation actions must be chosen from: insert, increase, replace, modify, clear. 3. For categorical points, explicitly list options: • Type: uint, address, bytes, struct, etc. • Visibility: public, internal, private.

4. Output format:

<mutation point> <name>...</name> <mutation rule>...</mutation rule> </mutation point>

Figure 6: Prompt for generating syntax-oriented mutation rules

LLM identifies features such as fixed-size array, struct, and internal function, among others. Syntax-Oriented Rule Generation. This step aims to generate syntax-oriented mutation rules, which are used to randomly mutate programs containing the aforementioned features. Since the mutation rules obtained by directly analyzing boundary conditions with the LLM can be vague, syntax-oriented rules also serve as references to guide the creation of more precise boundary-oriented rules. Figure 6 illustrates the prompt used for generating syntaxoriented rules. As each language feature consists of several mutation points, the first command in the prompt directs the LLM to list the mutation points associated with that feature. For instance, the struct includes mutation points such as its field types, visibility, and declaration location. As for mutation actions, we choose five representative types (insert, increase, replace, modify, and clear) as options for the LLM to select. Boundary-Oriented Rule Generation. This step aims to generate boundary-oriented mutation rules based on program features and syntax-oriented rules. A single code block may contain multiple boundary conditions, and each boundary condition can be triggered in multiple ways. Each triggering method can correspond to a mutation rule. To ensure the completeness of mutation rules, we select only one feature and one boundary condition in each prompt, thereby addressing Challenge 2. At the same time, to ensure the clarity of mutation rules, we use syntax-oriented rules to guide the generation of boundary-oriented rules, thereby addressing Challenge 1. Figure 7 illustrates the prompt used for generating the boundary-oriented mutation rule. In the prompt, the first task asks the LLM to analyze how the current boundaryrelated feature can trigger the specified boundary condition. The second task asks the LLM to either select from existing syntax-oriented rules or synthesize new ones based on them. After obtaining the rules, we need to categorize them. We use the features embedded in the prompt as labels, representing the major category to which each generated rule belongs. These features will be used in the subsection 4.2 for rule selection. Bowei Su et al.: Preprint submitted to Elsevier

Code snippet of compiler or executor: {source code} Code snippet of boundary condition: {boundary {boundary condition} condition Solidity language feature: {feature} Basic rules: {rule}

1. Analyze how program with {feature} can trigger boundary condition. 2. Select and generate two mutation rules from the syntaxoriented rules that can mutate a program with {feature} to trigger the boundary condition. 3. Output format: <mutation rule> <name>...</name> <description>...</ description > </mutation rule >

Figure 7: Prompt for generating boundary-oriented mutation rule Solidity program: {Solidity program} Candidate features: {features} Select two suitable features that may be included in the above program. Directly return the selected features without any explanation.

(a) Prompt for selecting features. Solidity program: {Solidity program} Candidate mutation rules: {rules}

Select one suitable mutation rule for the above program. Directly return the selected mutation rule without any explanation.

(b) Prompt for selecting mutation rule. Figure 8: Prompt for selecting features and mutation rule.

4.2. Fine-Grained Mutation After generating the mutation rules, we select the appropriate ones to apply mutations to the program. The process can be divided into three stages, namely rule selection, rule application, and selection optimization. During rule selection, ParityFuzz selects the most suitable mutation rules for each seed program. During rule application, ParityFuzz applies the selected rules to mutate the program and then fixes programs that fail to compile. During selection optimization, ParityFuzz leverages reinforcement learning to fine-tune the LLM, optimizing the rule selection process. Rule Selection. ParityFuzz employs two sets of mutation rules to perform mutations. Boundary-oriented rules are designed to uncover inconsistencies related to boundary conditions, while syntax-oriented rules ensure syntactic diversity of programs, thereby triggering inconsistencies related to the code blocks where boundary conditions reside. Figure 9 shows the prompt for selecting features and mutation rules. ParityFuzz first prompts the LLM to identify which features the seed program contains (Figure 8a), and then selects a specific mutation rule under the chosen feature (Figure 8b). Since mutation rules are based on the program already having certain features, this ensures that the selected rules are well-suited to the seed program. Rule Application. Figure 9 shows the prompt for mutating and repairing programs. After selecting the appropriate rules, ParityFuzz applies them to mutate the seed program (shown Page 6 of 15

Short Title of the Article Solidity program: {Solidity program} Mutation rules: {rules}

Mutate above Solidity program according to above mutation rules. Directly return the mutated Solidity program without any explanation.

(a) Prompt for mutating program. Solidity program: {Solidity program} Compilation error messages: {error messages}

Fix above Solidity program based on above error messages. Directly return the fixed Solidity program without any explanation.

(b) Prompt for repairing program.

4.3. Execution-Based Detection

Figure 9: Prompt for mutating and repairing program.

in Figure 9a). If the generated program fails to compile with Solc, ParityFuzz provides the error message to the LLM to repair the program (shown in Figure 9b). While the repair process may alter the program’s original semantics, it helps uncover more cross-compiler inconsistencies. Programs generally need to be compilable with Solc in order to potentially trigger inconsistencies. Programs that cannot compile with Solc often also fail on other Solidity compilers and therefore cannot reveal cross-compiler differences. Repairs also enable the generation of programs with complex syntactic structures that exercise boundary conditions and are more likely to reveal inconsistencies. Generating such complex structures in a single mutation step can be challenging for the LLM, so iterative repair is applied to ensure successful program mutations. Rule Selection Optimization. To optimize the rule selection process, ParityFuzz leverages reinforcement learning to finetune the LLM. Inspired by previous studies [11, 47], we finetune our model using the GRPO algorithm [32]. Compared to the PPO [31] algorithm used in CovRL-Fuzz[11], GRPO does not require a separate value model, saving both memory and computational cost. Additionally, ParityFuzz does not adopt a reward model like the one in CovRL-Fuzz[11]; instead, it uses a reward function to score the outputs of the LLM. Since the reward function itself can produce precise scores, the step of training a reward model using the reward function can be eliminated. The reward function uses the results of inconsistency detection to calculate the score of the mutated program. This score indirectly reflects the quality of the selected mutation rules. The score is calculated as follows. ⎧1, ⎪ ⎪−1, score = ⎨ ⎪−1, ⎪𝑅covDiff , ⎩

if an inconsistency occurs. if the inconsistency is a false positive. if Solc fails to compile otherwise. (1)

If the new program triggers an inconsistency, the score is 1. If the inconsistency is a false positive, a penalty is applied with a score of -1. If no inconsistency is triggered Bowei Su et al.: Preprint submitted to Elsevier

and Solc fails to compile the program, a penalty of -1 is also applied. Otherwise, the new program is successfully compiled by Solc but does not trigger any inconsistencies, and the score is determined by the change in coverage. The coverage difference is calculated as follows: compile the programs before and after mutation using Solc separately, measure the coverage within the Solc compiler for each version, and then compute the difference. The coverage difference ranges from -1 to 1, which helps guide the LLM to generate programs with richer syntax through mutation. To uncover inconsistencies, we compare the compilation and execution results of programs across different compilers. While using multiple compilers introduces intentional design differences, ParityFuzz ensures the reliability of its differential oracle through two mechanisms. First, we select Solc as the baseline compiler, as it is the most widely used reference compiler for Solidity. Second, we systematically eliminate inconsistencies caused by documented design differences using compiler-provided documentation and pattern matching, so that only unexpected or potentially erroneous behaviors are detected. This combination allows ParityFuzz to distinguish true inconsistencies from expected behavior in a reproducible manner. Compilation Result Comparison. As shown in subsection 3.3, compilation result inconsistencies include error message inconsistency and compilation status inconsistency. Both inconsistencies can be detected by directly comparing the outputs of the compilers. Error message inconsistency means that both compilers produce errors, but one outputs a clear error message while the other does not. A clear error message typically begins with the keyword "error," followed by the location in the program that caused the error and the reason for the failure. Therefore, ParityFuzz can identify clear error messages by matching keywords. Compilation status inconsistency means that one compiler can compile the code successfully while another fails to compile it. This inconsistency can be detected by directly comparing the outputs of the compiler. When compilation succeeds, the compiler produces bytecode; when it fails, the compiler throws an error. Execution Result Comparison. Execution result inconsistencies include execution status inconsistency and execution output inconsistency. Both inconsistencies can be detected by directly comparing the outputs of the executors. Execution status inconsistency means that a program can be successfully executed after being compiled by one compiler but fails after being compiled by another. If the executor produces an error, it indicates a runtime failure; otherwise, the program executes successfully. Execution output inconsistency means that the outputs of the executor differ after executing the two programs. As shown in Table 1, ParityFuzz supports detecting execution result inconsistencies for four compilers (i.e., Solc, Revive, Zksolc, Solang). Among them, only the virtual machine Page 7 of 15

Short Title of the Article

(i.e., SVM) paired with Solang has a different output format compared to the other virtual machines (i.e., EVM, PolkaVM, ZKEVM). The output of SVM is in borsh [23] format, while the outputs of the other three virtual machines are in ABIencoded [36] format. For the former, ParityFuzz extracts specific data from the Borsh class. For the latter, ParityFuzz uses ABI decoder to decode the output. The result is two outputs that can be compared. Comparison Target Selection. Due to varying levels of compatibility between different Solidity compilers and Solc, customized handling is required when performing comparisons. As shown in Figure 1, both Revive and Zksolc rely on Solc to generate Yul programs, which are then compiled downstream. Solar aims to optimize the performance of Solc. Therefore, these three compilers (i.e., Revive, Zksolc, and Solar) are highly compatible with Solc and are expected to produce the same compilation and execution results, making direct comparison feasible. Solang and Sold have limited compatibility with Solc and do not support many Solidity language features. As a result, a large number of programs can be successfully compiled and executed with Solc, whereas Solang and Sold tend to trigger errors. Therefore, to eliminate such meaningless inconsistencies, we apply the following restriction. As for compilation status inconsistency, only when the error messages from Solang and Sold are unclear do we consider the inconsistency to be valid. As for execution status inconsistency, we only consider cases where the program executes correctly when compiled with Solang, but fails during execution when compiled with Solc. False Positive Elimination. This part aims to eliminate false positives among the detected inconsistencies. Each Solidity compiler provides a document describing its designlevel differences from Solc (e.g., Revive [27], Zksolc [55], Solang [33], Sold [17]), specifically indicating which syntactic features are handled differently and the expected compilation or execution outcomes. We consider an inconsistency a false positive if the responsible syntactic feature has a clearly defined expected outcome in the compiler’s document. Features mentioned without precise expected behavior are not filtered, ensuring that potential new or subtle inconsistencies remain detectable. To implement this, we manually extract syntactic features from these documents and encode them as patterns. During inconsistency detection, ParityFuzz performs automated pattern matching on input programs: if a program matches a documented feature pattern, the corresponding inconsistency is filtered out. This semi-automated approach reduces false positives while preserving novel or unexpected behaviors, providing a reproducible and reliable method.

Bowei Su et al.: Preprint submitted to Elsevier

5. Implementation Implementation of ParityFuzz. We use Python to implement ParityFuzz. The compiler versions used in ParityFuzz are as follows: Solc[5] (0.8.28), Revive[44] (v0.1.0dev.9), Zksolc[20] (v1.5.10), Solang[34] (v0.3.3), Sold[18] (0.77.0), and Solar[26] (0.1.1). The execution environments for Revive and Solang are derived from modifications to the test environments ([43, 35]) within their respective compiler projects. These test environments are originally designed for unit tests where Solidity contracts are defined directly in the test code. To support differential testing in ParityFuzz, we adapt them to execute arbitrary Solidity programs. Specifically, we extend the environments to accept the Solidity file, contract name, and function name as inputs. We also normalize runtime outputs to enable automated comparison across compilers. For the other compilers, Zksolc and Sold directly use their respective node-based execution environments[19, 16]. However, due to a bug in Sold’s node, it cannot run correctly. Therefore, in subsequent experiments, we do not test execution-related inconsistencies for Sold. In addition, Solar does not support bytecode generation. Therefore, execution-related inconsistencies for Solar are not considered. As for LLM, we use ChatGPT-4o to generate mutation rules, Qwen2.5-Coder-7B-Instruct[30] to select features, Qwen2.5-Coder-0.5B-Instruct[29] to select mutation rules, and Qwen-coder-plus[28] to apply the mutations to the program. The reasons for selecting the LLM are as follows. rule generation requires a model capable of analyzing source code, such as ChatGPT-4o. Feature selection demands the LLM to identify key features of a program; tests show that Qwen models under 7B parameters cannot meet this need. Rule selection is size-independent, so a smaller model is used for easier fine-tuning. Program mutation, requiring strong code understanding, is suited for models like Qwen-coder-plus. Implementation of compared fuzzers. We selected the following fuzzers for comparison: POLYGLOT [3], aflcompiler-fuzzer [14], FUZZOL [22], solc_ossfuzz of OSSFUZZ [4]. We first introduce the characteristics of these fuzzers. As for program mutation, POLYGLOT modifies the IR of the program, Fuzzol alters the AST of the program, solc_ossfuzz applies string-level mutations (bit flipping, byte insertion, and deletion), and afl-compiler-fuzzer decomposes and reassembles the program. For the bug detection module, POLYGLOT, afl-compiler-fuzzer, and FUZZOL all use compilation crashes as oracles. solc_ossfuzz determines whether the program compiled by Solc can execute correctly. Therefore, in our task, these fuzzers can only detect inconsistencies in the compilation results. The configuration for these fuzzers is as follows: The timeout is set to two seconds for all fuzzers. afl-compiler-fuzzer includes two program mutation methods (text-mutation and splice-mutation). We set their triggering probabilities to 50% each.

Page 8 of 15

Short Title of the Article

6. Evaluation This section presents a comprehensive evaluation of ParityFuzz, guided by the following research questions: • RQ1: How effective is ParityFuzz at detecting inconsistencies across different Solidity compilers? • RQ2: How does the performance of ParityFuzz compare to state-of-the-art Solidity fuzzers? • RQ3: What is the contribution of each of the main components of ParityFuzz to its overall effectiveness? Environment Setup. All experiments were conducted on a Linux server equipped with an Intel Xeon Platinum 8360H CPU (3.00GHz) and 125 GB of RAM, running Ubuntu 22.04. An NVIDIA GeForce RTX 4090 GPU was used for local model inference and fine-tuning of Qwen2.5-Coder-0.5BInstruct. All other LLMs were accessed via their respective APIs. Dataset. Our initial benchmark dataset was constructed from three sources: the dataset from Ma et al. [21], the official Solidity historical bugs [6], and the official Solidity test suite [7]. This resulted in a corpus of 4,260 Solidity programs that successfully compile with the Solc compiler.

6.1. Inconsistencies Identification As shown in Table 2, ParityFuzz has successfully identified 64 inconsistencies across six Solidity compilers, including 15 from Revive, 9 from Zksolc, 34 from Solang, 4 from Sold, and 2 from Solar. Detailed information on the inconsistencies can be found in our repository [37]. All identified inconsistencies have been reported, with approximately one-third confirmed by the developers. At the time of writing, 11 inconsistencies had been fixed by developers, and inconsistencies related to Revive received a bug bounty from the developers. Meanwhile, we have made the following observations. Firstly, Solang has the most inconsistencies of all types. In Table 2, its ESI count is only 3 because we only counted cases where Solc failed but Solang did not. The reverse cases were too many to include. The high inconsistency rate is mainly due to limited maintenance. Among the many inconsistencies we reported, only two issue [38, 39] has been acknowledged and fixed. Secondly, inconsistent execution results can be resolved not only by fixing the executor, but also by applying changes to the compiler or improving the documentation. For instance, due to ZKEVM not supporting ripemd160, the execution of ripemd160("") returns 0. To fix this issue [40], the developers chose to throw an error during compilation. Another example is that Solc defaults to the EVM legacy codegen, whereas Zksolc uses the Yul codegen by default. This discrepancy may cause different execution results for certain programs. To resolve the issue [41], the developers clarified this behavior in the documentation. In the following case studies, we analyze representative inconsistencies to demonstrate how ParityFuzz detects them and to examine their associated security consequences. Bowei Su et al.: Preprint submitted to Elsevier

Table 2 Inconsistency detection results of ParityFuzz. Inconsistency Compiler Revive Zksolc Solang Sold Solar Total

Total

EMI

CSI

ESI

EOI

Reported

Confirmed

Fixed

0 0 0 0 0 0

2 0 23 4 2 31

7 4 3 0 0 14

6 5 8 0 0 19

15 9 34 4 2 64

15 8 2 0 2 27

5 2 2 0 2 11

Case Study 1: Delegating a Call to an Invalid Address in Revive. As shown in Figure 11, the program fails to execute when compiled with Revive, but runs successfully when compiled with Solc. Figure 12 illustrates how the executor of Revive handles delegatecall. As we can see, for invalid addresses, the executor of Revive throws an error, causing the program execution to fail. The way ParityFuzz detects this inconsistency is as follows. In rule generation 4.1, ParityFuzz uses LLM to analyze the source code in Figure 12 and generates the mutation rule: "replace delegatecall with invalid contract type". Then, during the rule selection phase, ParityFuzz successfully selects this rule for mutation. This mutation rule transforms the delegatecall target from a variable passed as a parameter (Figure 10, line 3) into an invalid address (Figure 11, line 4), thereby triggering the inconsistency. Therefore, ParityFuzz can extract the boundary conditions in the source code very well and generate corresponding mutated rules to implement mutation. This inconsistency can cause critical functionality to be blocked in real-world deployments. A contract that behaves correctly on Ethereum (compiled with Solc) may exhibit ambiguous behavior when migrated to Polkadot (compiled by Revive). If there are critical operations following a delegatecall, Revive may fail the delegatecall due to an invalid address, preventing any subsequent operations from being executed. This behavior can potentially be exploited by an attacker to block contract functionality or launch a DoS attack. Case Study 2: Deleting an Array Element Causes the Array to Be Deleted in Solang. As shown in Figure 14, when executing this program, the executor of Solc outputs 2, while the executor of Solang outputs 0. This difference occurs because deleting an array element in Solang removes the entire array, causing its length to be reset to zero. The inconsistency is detected by ParityFuzz as follows. During the rule selection phase, ParityFuzz successfully selectes the rule "insert delete operation to remove array elements" for mutation. This rule effectively transformed a full-array deletion (Figure 13, line 6) into the deletion of specific array elements (Figure 14, lines 6–7), thereby triggering the inconsistency. This inconsistency can lead to severe financial losses. Consider a token contract that uses an array to record users’ token balances. When a user withdraws all their tokens, the contract deletes that user’s entry in the array. In Solang, deleting an element in the array causes the entire array to be Page 9 of 15

Short Title of the Article contract C { function delegateToLibrary ( address libraryAddress ) external returns ( bool ) { 3 ( bool success , ) = libraryAddress . delegatecall ( 4 abi . encodeWithSignature ( " targetFunction () ")); 5 return success ; 6 } 7} 1 2

contract C { function len () public returns ( uint ret ) { 3 uint [] memory data = new uint [](2) ; 4 data [0] = 234; 5 data [1] = 123; 6 delete data ; 7 assembly { 8 ret := mload ( data ) 9 } 10 } 11 } 1 2

Figure 10: The original version of the program in Figure 11 Figure 13: The original version of the program in Figure 14 contract C { 2 function delegateToLibrary ( address libraryAddress ) external returns ( bool ) { 3 address invalidAddress = address (0 x1 ) ; 4 ( bool success , ) = invalidAddress . delegatecall ( 5 abi . encodeWithSignature ( " targetFunction () ")); 6 return success ; 7 } 8} 1

Figure 11: An example of execution status inconsistency. The executor of Solc runs successfully, while the executor of Revive fails. fn delegate_call (...) -> ... { // ... 3 let code_hash = ContractInfoOf :: <T >:: get (& address ) 4 . ok_or ( Error :: <T >:: CodeNotFound ) 5 . map (| c| c. code_hash ) ?; 6 let executable = E :: from_storage ( code_hash , self . gas_meter_mut () ) ?; 7 // ... 8} 1

contract C { function len () public returns ( uint ret ) { 3 uint [] memory data = new uint [](2) ; 4 data [0] = 234; 5 data [1] = 123; 6 delete data [0]; 7 delete data [1]; 8 assembly { 9 ret := mload ( data ) 10 } 11 } 12 } 1 2

Figure 14: An example of execution output inconsistency. The executor of Solc outputs 2, while the executor of Solang outputs 0.

2

Figure 12: The source code of the executor in Revive that handles delegatecall

deleted, potentially removing all users’ balance records. As a result, the balances of other users may be lost, demonstrating a serious risk in financial applications. RQ1: ParityFuzz has found 64 inconsistencies across six Solidity compilers, 27 of which have been confirmed, and 11 of which have been fixed due to our Git issues. Notably, the inconsistency related to Revive received a bounty reward from the Polkadot community.

Bowei Su et al.: Preprint submitted to Elsevier

6.2. Comparison with SOTA Fuzzers We compare ParityFuzz with the other four Solidity compiler fuzzers (i.e., POLYGLOT, afl-compiler-fuzzer, FUZZOL, and solc_ossfuzz). The comparison focuses on inconsistency detection capability and the quality of generated programs. Unique Inconsistencies. Since other fuzzers cannot detect execution result inconsistencies, we only compare the number of detected compilation result inconsistencies. To avoid the interference of seed programs, we only count novel inconsistencies, meaning those that cannot be triggered before mutation but can be triggered after mutation. Only such novel inconsistencies are considered contributions of the fuzzer. We configure each fuzzer with the complete dataset, then run them separately for 4 days and collect the results. As shown in Table 3, ParityFuzz is able to detect more compilation result inconsistencies. Only POLYGLOT detected EMI, and the triggering program is shown in Figure 16. This program causes Solc to report an error without a clear message, while Sold and Solang provide clear error messages. Figure 15 shows the original program. POLYGLOT successfully triggered the Solc error by adding public before constant. ParityFuzz fails to detect this inconsistency because it does Page 10 of 15

Short Title of the Article Table 3 Inconsistency detection results of different fuzzers. Before|after the slash represents the count of EMI and CSI, respectively.

POLYGLOT afl-compiler-fuzzer FUZZOL solc_ossfuzz ParityFuzz

Revive

Zksolc

Solang

Sold

Solar

-|2

-

1|2 -|1 -|23

1|-|1 -|1 -|4

-|1 -|1 -|2

Total 2|3 -|3 -|1 -|31

not successfully select the specific mutation operation "add the visibility of a state variable with public". However, ParityFuzz identified many other inconsistencies, which also demonstrates its superiority. interface A { function f () external ;} contract B { function g () public {}} 3 contract C is B { 4 function h () external {} 5 bytes4 constant s1 = A . f . selector ; 6 bytes4 constant s2 = B . g . selector ; 7 bytes4 constant s3 = this . h . selector ; 8 bytes4 constant s4 = super . g . selector ; 9} 1

Percentage

Compiler Fuzzers

90 Compilation Success (%) 79.70 80 Coverage (%) 70 60 50 36.16 40 31.44 30.12 27.77 30 26.50 20.07 20 9.10 10 5.00 4.40 0 LOT

POLYG

zzer FUZZOL

piler-fu

afl-com

Fuzzers

sfuzz

solc_os

zz

u ParityF

Figure 17: The quality of 1000 programs generated by different fuzzers.

2

Figure 15: The original version of the program in Figure 16 interface A { function f () external ;} contract B { function g () public {}} 3 contract C is B { 4 function h () external {} 5 bytes4 constant s1 = A . f . selector ; 6 bytes4 public constant s2 = B . g . selector ; 7 bytes4 constant s3 = this . h . selector ; 8 bytes4 constant s4 = super . g . selector ; 9} 1 2

Figure 16: An example of error message inconsistency. Solc produces a compilation error without a clear error message, while Sold and Solang provide clear error messages.

Quality of Generated Program. Most inconsistencytriggering programs can be successfully compiled by Solc. Cases like the one shown in Figure 16 are rare, as Solc is already a mature and widely adopted compiler. Inconsistencytriggering programs also exhibit diversity, which can be evaluated through coverage. Therefore, we assess the quality of generated programs using both compilation success rate and code coverage. Since there are differences in the program generation speeds of different fuzzers, directly comparing them would lead to inaccurate quality evaluation. Therefore, we randomly selected 100 programs from the dataset, performed mutations for a period of time, and generated 1,000 new programs. We then evaluated the quality of these 1,000 programs. Bowei Su et al.: Preprint submitted to Elsevier

As shown in Figure 17, ParityFuzz achieves the highest compilation success rate and coverage rate. ParityFuzz repairs the generated programs, which greatly increases the compilation success rate. ParityFuzz uses two sets of mutation rules to mutate the program, thereby generating complex programs and improving coverage. Compared to the second-ranked POLYGLOT, although ParityFuzz achieves only a 15.02% increase in coverage, it is able to detect more inconsistencies. This is because ParityFuzz performs mutations based on the existing syntactic features of the program, and therefore does not cause drastic changes in coverage. For instance, the original program in Figure 18 has a coverage of 15.71%, while the mutated version in Figure 19 has a coverage of 16.92%. Although the increase in coverage is limited, the mutation successfully produces a program that triggers an inconsistency. RQ2: Compared to other fuzzers, ParityFuzz can detect more inconsistencies, while also generating programs with higher compilation success rates and greater coverage.

6.3. Ablation Study . To understand the contributions of each module, we use the following variants of ParityFuzz to conduct ablation studies. • ParityFuzzbor, w/o RL uses boundary-oriented rules to perform mutation, while eliminating the impact of reinforcement learning fine-tuning. • ParityFuzzsor, w/o RL uses syntax-oriented rules to perform mutation, while eliminating the impact of reinforcement learning fine-tuning. • ParityFuzzdr, w/o RL uses direct rules to perform mutation, while eliminating the impact of reinforcement learning fine-tuning. Direct rules are obtained by Page 11 of 15

Short Title of the Article contract C { 2 struct S { 3 uint x; 4 } 5 struct T { 6 S s; 7 uint y; 8 } 9 function test () pure public { 10 S memory inner = S (43) ; 11 T memory outer = T ( inner , 512) ; 12 assert ( outer . y == 512) ; 13 assert ( outer . s . x == 43) ; 14 assert ( outer . s . x == 42) ; 15 } 16 } 1

Figure 18: The original version of the program in Figure 19 contract C { struct S { 3 bytes8 val ; 4 } 5 struct U { 6 S [] elems ; 7 mapping ( uint = > int ) attr ; 8 } 9 function example () public { 10 U storage u ; 11 assembly { 12 u. slot := 0 13 } 14 u. elems . push () ; 15 u. attr [0] = 5; 16 } 17 } 1 2

Figure 19: An example of compilation status inconsistency. Compilation error with Solang vs success with Solc.

using the LLM to directly analyze code blocks that contain boundary conditions, with no extra prompt information(i.e., specific boundary condition code, program feature, and syntax-oriented rules). To be compatible with the selection strategy of ParityFuzz, we classify direct rules using the LLM. This allows us to first select an rule category, and then choose a specific direct rule within that category. • ParityFuzzr uses a purely random strategy to select mutation rules. • ParityFuzzw/o RL eliminates the impact of reinforcement learning fine-tuning.

Table 4 Comparison of inconsistency detection capability with the five variants of ParityFuzz Approach.

Inconsistency

ParityFuzzbor, w/o RL ParityFuzzsor, w/o RL ParityFuzzdr, w/o RL ParityFuzzr ParityFuzzw/o RL ParityFuzz

14 13 11 12 14 17

three steps (shown in Figure 5) are more effective than those obtained by directly analyzing the source code in a single step. ParityFuzzsor, w/o RL and ParityFuzzbor, w/o RL are designed to study whether syntax-oriented and boundary-oriented rules are beneficial for generating inconsistency-triggering programs. ParityFuzzr is designed to investigate whether the rule selection strategy of ParityFuzz outperform a purely random strategy. ParityFuzzw/o RL is designed to evaluate whether LLMs fine-tuned via reinforcement learning can better select mutation rules. We randomly selected 1,000 programs from the dataset for the ablation study. The five variants and ParityFuzz each performed one round of mutation. Table 4 presents the comparison results between the variants and ParityFuzz. Contribution of Rule Generation. As shown in Table 4, compared to ParityFuzzdi, w/o R , ParityFuzzbor, w/o RL detects more inconsistencies. This indicates that the three-step approach (Figure 5) generates more concise and clearer mutation rules, which better guide programs to trigger boundary cases. In contrast, ParityFuzzdi, w/o R combines analysis and generation in a single prompt, often resulting in vague or incomplete rules. Both ParityFuzzbor, w/o RL and ParityFuzzsor, w/o RL are able to detect a large number of inconsistencies, indicating that boundary-oriented rules and syntax-oriented rules are effective. ParityFuzzw/o RL , which combines both sets of mutation rules, is still capable of detecting many inconsistencies, thereby validating the effectiveness of the two sets of mutation rules. Contribution of Rule Selection. Compared to ParityFuzzw/o RL , ParityFuzzr detects fewer inconsistencies. This is because randomly selected mutation rules may not be applicable to the seed programs. For instance, if a seed program lacks a certain syntactic feature required by a mutation, the resulting program may be invalid. Contribution of Fine-tuning. Compared to ParityFuzzw/o RL , ParityFuzz detects more inconsistencies. This indicates that the fine-tuned LLM is able to select mutation rules that better match the seed programs. RQ3: Each component of ParityFuzz contributes to detecting more inconsistencies.

Among these variants, ParityFuzzdi, w/o R is used to investigate whether boundary-oriented rules generated through Bowei Su et al.: Preprint submitted to Elsevier

Page 12 of 15

Short Title of the Article

7. Discussion Threats to Validity. One threat to validity may stem from slight discrepancies between the execution environment used by ParityFuzz and the actual environments in which blockchains are deployed. To ensure execution efficiency, ParityFuzz prioritizes the use of executors bundled with Solidity compiler projects (e.g., Revive, Solang). These executors can be compiled into executable files to efficiently execute input bytecode. In practical applications, the execution environment is typically a node, which provides a more comprehensive set of functionalities. For example, the executor bundled with Revive does not support precompiled contracts, causing the return value of ripemd160 to always be 0, while such functionality is supported by nodes in the blockchain mainnet. Another threat to validity arises from the fact that some seed programs may trigger inconsistencies without mutation. Therefore, when comparing different fuzzers, we only considered programs where inconsistencies are triggered after mutation. After accounting for the influence of such seed programs, ParityFuzz still detects a significant number of inconsistencies. As a result, the impact of this threat can be considered negligible. Finally, as ParityFuzz relies on LLM-based program mutation and repair, the nondeterministic nature of LLM outputs could introduce variability in the generated programs. To mitigate this, we fix both the random seed and the temperature of the LLM during mutation and repair, ensuring reproducibility. In practice, multiple runs on the same seed program consistently detect the same cross-compiler inconsistencies, indicating that LLM nondeterminism has a limited impact on our results. Limitations. ParityFuzz supports various Solidity compilers, demonstrating strong versatility. However, there are two factors that may weaken its generality. First, false positive elimination requires manual intervention. We must manually summarize the Solidity program features that may lead to discrepancies based on the documentation. However, since the relevant documentation is relatively concise, introducing a new compiler does not demand significant human resources. Second, some compilers may lack readily available execution environments. For example, the Revive and Solang compilers do not directly provide executors, requiring manual modification of their test programs to wrap them into usable executors. Meanwhile, the Sold compiler relies on a test node as its execution environment, but certain bugs currently prevent it from functioning properly.

8. Related Work Fuzz testing is one of the most effective techniques for uncovering compiler inconsistencies. It typically involves two main steps: program generation and bug detection. Program Generation. Several tools [51, 46, 4] can generate syntax-correct programs from Protobuf specifications [13]. In addition, there are also several mutation-based tools. FUZZILLI [15] and POLYGLOT [3] mutate the Intermediate Bowei Su et al.: Preprint submitted to Elsevier

Representation of seed programs to generate new programs. While Superion [48], GrayC [12], and FUZZOL [22] mutate the Abstract Syntax Tree of seed programs. AFL-compilerfuzzer [14] decomposes seed programs into small parts and then randomly substitutes them with others to generate new programs. At the same time, LLMs are also widely used for program generation. FuzzGPT [10] leverages LLMs to generate uncommon input programs for effective fuzzing. WhiteFox [53] employs LLMs to analyze optimization source code and produce test programs that precisely trigger specific optimizations. Building on the idea of using LLMs for program generation, LLM4CBI [47] further incorporates both data-flow and control-flow analyses to craft more precise prompts for generating targeted programs. Beyond direct program generation, LLMs can also be utilized to synthesize generators or mutators, which in turn are used to produce test programs. MetaMut [25] uses LLMs to create 118 semanticaware mutators, which can perform diverse mutations on programs. CKGFuzzer [52] uses LLMs to automatically generate fuzz drivers and perform data flow analysis to produce input seeds, thereby achieving fully automated fuzz testing. While these approaches are effective for general fuzzing or optimization testing, they are not explicitly designed to trigger cross-compiler inconsistencies. In contrast, ParityFuzz generates fine-grained mutation rules by analyzing the source code of compilers and executors, specifically targeting programs that are likely to expose inconsistencies across compilers. Differential Analysis. Differential testing is a commonly used approach for detecting inconsistencies. Some tools [49, 4, 1] detect compiler bugs by comparing outputs generated under different optimization levels within the same compiler, while others [46, 24, 2] do so by comparing the outputs of different compilers. There are also studies focusing on eliminating false positives during the detection of inconsistencies. Tools such as [42, 50] define various patterns that are likely to cause false positives, and deliberately avoid generating such programs during the program generation phase. FuzzJIT [49] creates a blacklist of disturbing APIs and block their generation during mutation. Similar to existing work [46, 24, 2], ParityFuzz detects inconsistencies across different compilers or executor. The outputs from compilers can be directly compared, whereas for outputs generated by the executor, ParityFuzz performs a comparison by first decoding the data. In ParityFuzz, various program patterns that may lead to false positives are manually defined, and ParityFuzz eliminates false positives through pattern matching.

9. Conclusion This paper proposes ParityFuzz, a cross-compiler differential testing tool to detect inconsistencies among different Solidity compilers. To generate test programs that can lead to inconsistencies, ParityFuzz generates fine-grained mutation rules by analyzing source code (i.e., compiler and execution Page 13 of 15

Short Title of the Article

environment). Then, to select the most suitable mutation rules for seed programs, ParityFuzz first chooses a rule category, and then selects a specific rule within that category. Meanwhile, ParityFuzz uses reinforcement learning to optimize the selection process. Finally, to detect inconsistencies, ParityFuzz compares the results of multiple compilations and executions. Evaluation results show that ParityFuzz has found 64 inconsistencies among six Solidity compilers. Following our responsible disclosure of the identified inconsistencies, 11 of the newly discovered inconsistencies have been successfully fixed by the community, and the inconsistencies related to Revive received a bounty reward from the Polkadot community. As a next step, we compare ParityFuzz with existing state-of-the-art fuzzers to assess its relative effectiveness. Our experiments show that ParityFuzz is more effective in generating high-quality test programs (up to 18× improvement in compilation success rate, 1.8× improvement in coverage) and detecting inconsistencies than state-of-theart fuzzers (31× more inconsistencies).

References

[3] Chen, Y., Zhong, R., Hu, H., Zhang, H., Yang, Y., Wu, D., Lee, W., 2021. One engine to fuzz’em all: Generic language processor testing with semantic validation, in: 2021 IEEE Symposium on Security and Privacy (SP), IEEE. pp. 642–658. [4] Collective, A., 2024. Oss-fuzz. https://github.com/ethereum/ solidity/tree/develop/test/tools/ossfuzz. [5] Collective, A., 2026a. Solc. https://github.com/ethereum/solidity. [6] Collective, A., 2026b. Solidity history bugs. https://github.com/ ethereum/solidity/blob/develop/docs/bugs.json. [7] Collective, A., 2026c. Solidity test programs. https://github.com/ ethereum/solidity/tree/develop/test. [8] DaniPopes, G.K., 2024. Introducing solar. https://www.paradigm.xyz/ 2024/11/solar. [9] DefiLlama, 2025. Defillama - defi dashboard. https://defillama.com/. [10] Deng, Y., Xia, C.S., Yang, C., Zhang, S.D., Yang, S., Zhang, L., 2023. Large language models are edge-case fuzzers: Testing deep learning libraries via fuzzgpt. arXiv preprint arXiv:2304.02014 . [11] Eom, J., Jeong, S., Kwon, T., 2024. Fuzzing javascript interpreters with coverage-guided reinforcement learning for llm-based mutation, in: Proceedings of the 33rd ACM SIGSOFT International Symposium on Software Testing and Analysis, pp. 1656–1668. [12] Even-Mendoza, K., Sharma, A., Donaldson, A.F., Cadar, C., 2023. Grayc: Greybox fuzzing of compilers and analysers for c, in: Proceedings of the 32nd ACM SIGSOFT International Symposium on Software Testing and Analysis, pp. 1219–1231. [13] Google, 2024. Protocol buffers. https://protobuf.dev/. [14] Groce, A., van Tonder, R., Kalburgi, G.T., Le Goues, C., 2022. Making no-fuss compiler fuzzing effective, in: Proceedings of the 31st ACM SIGPLAN International Conference on Compiler Construction, pp. 194–204. [15] Groß, S., Koch, S., Bernhard, L., Holz, T., Johns, M., 2023. Fuzzilli: Fuzzing for javascript jit compiler vulnerabilities., in: NDSS. [16] Labs, E., 2023. ever-node. https://github.com/everx-labs/ever-node. [17] Labs, E., 2024a. Differences between sold and solc. https:// github.com/everx-labs/TVM-Solidity-Compiler/blob/master/API.md. [18] Labs, E., 2025. Sold. https://github.com/everx-labs/TVM-SolidityCompiler. [19] Labs, M., 2024b. era-test-node. https://github.com/matter-labs/eratest-node. [20] Labs, M., 2026. Zksolc. https://github.com/matter-labs/eracompiler-solidity. [21] Ma, H., Zhang, W., Shen, Q., Tian, Y., Chen, J., Cheung, S.C., 2024. Towards understanding the bugs in solidity compiler, in: Proceedings of the 33rd ACM SIGSOFT International Symposium on Software Testing and Analysis, pp. 1312–1324. [22] Mitropoulos, C., Sotiropoulos, T., Ioannidis, S., Mitropoulos, D., 2023. Syntax-aware mutation for testing the solidity compiler, in: European Symposium on Research in Computer Security, Springer. pp. 327–347. [23] NEAR, 2025. borsh. https://borsh.io/. [24] Ofenbeck, G., Rompf, T., Püschel, M., 2016. Randir: differential testing for embedded compilers, in: Proceedings of the 2016 7th ACM SIGPLAN Symposium on Scala, pp. 21–30. [25] Ou, X., Li, C., Jiang, Y., Xu, C., 2024. The mutators reloaded: Fuzzing compilers with large language model generated mutation operators, in: Proceedings of the 29th ACM International Conference on Architectural Support for Programming Languages and Operating Systems, Volume 4, pp. 298–312. [26] Paradigm, 2025. Solar. https://github.com/paradigmxyz/solar. [27] Polkadot, 2026. Differences between revive and solc.

[1] Bernhard, L., Scharnowski, T., Schloegel, M., Blazytko, T., Holz, T., 2022. Jit-picking: Differential fuzzing of javascript engines, in: Proceedings of the 2022 ACM SIGSAC Conference on Computer and Communications Security, pp. 351–364. [2] Chen, Y., Su, T., Sun, C., Su, Z., Zhao, J., 2016. Coverage-directed differential testing of jvm implementations, in: proceedings of the 37th ACM SIGPLAN Conference on Programming Language Design and Implementation, pp. 85–99.

https://contracts.polkadot.io/revive_compiler/differences_ yul_translation/. [28] Qwen, 2026a. Qwen-coder-plus. https://bailian.console.aliyun. com/?tab=model#/model-market/detail/qwen-coder-plus. [29] Qwen, 2026b. Qwen2.5-coder-0.5b-instruct. https://huggingface.co/ Qwen/Qwen2.5-Coder-0.5B-Instruct. [30] Qwen, 2026c. Qwen2.5-coder-7b-instruct. https://huggingface.co/ Qwen/Qwen2.5-Coder-7B-Instruct.

CRediT authorship contribution statement Bowei Su: Methodology, Validation, Writing – original draft. Mingxi Ye: Methodology, Validation, Writing – review and editing. Yuhong Nan: Methodology, Resources, Writing – review and editing. Peilin Zheng: Data curation, Software, Writing – review and editing. Zibin Zheng: Investigation, Validation, Writing – review and editing.

Declaration of competing interest The authors declare that they have no known competing financial interests or personal relationships that could have appeared to influence the work reported in this paper.

Acknowledgments This work is supported by NSFC/RGC Collaborative Research (62461160332), Natural Science Foundation of China (62032025, 624B2139) and Guangdong Zhujiang Talent Program (2023QN10X561).

Data availability The source code and experimental results are publicly available at: https://zenodo.org/records/19944888

Bowei Su et al.: Preprint submitted to Elsevier

Page 14 of 15

Short Title of the Article [31] Schulman, J., Wolski, F., Dhariwal, P., Radford, A., Klimov, O., 2017. Proximal policy optimization algorithms. arXiv preprint arXiv:1707.06347 . [32] Shao, Z., Wang, P., Zhu, Q., Xu, R., Song, J., Bi, X., Zhang, H., Zhang, M., Li, Y., Wu, Y., et al., 2024. Deepseekmath: Pushing the limits of mathematical reasoning in open language models. arXiv preprint arXiv:2402.03300 . [33] Solang, 2023. Differences between solang and solc. https:// [34] [35] [36]

solang.readthedocs.io/en/v0.3.3/targets/solana.html#solidityfor-solana-incompatibilities-with-solidity-for-ethereum. Solang, H., 2025a. Solang. https://github.com/hyperledger-solang/ solang. Solang, H., 2025b. Test cases in solang. https://github.com/ hyperledger-solang/solang/tree/main/tests/solana_tests. Solidity, 2025. Contract abi specification. https://docs.soliditylang. org/en/latest/abi-spec.html. Su, B., 2025. Parityfuzz. https://zenodo.org/records/19944888.

[37] [38] Subway2023, 2025a. Decoding an empty tuple causes error in solang. https://github.com/hyperledger-solang/solang/issues/1727. [39] Subway2023, 2025b. Deleting an array element causes the array to be deleted in solang. https://github.com/hyperledger-solang/solang/ issues/1785. [40] Subway2023, 2025c. ripemd160 is unavailable in zksolc. https:// github.com/matter-labs/era-compiler-solidity/issues/275. [41] Subway2023, 2025d. Zksolc default codegen problem. https:// github.com/matter-labs/era-compiler-solidity/issues/272. [42] Sun, C., Le, V., Su, Z., 2016. Finding and analyzing compiler warning defects, in: Proceedings of the 38th International Conference on Software Engineering, pp. 203–213. [43] Technologies, P., 2025. Test cases in revive. https://github.com/ paritytech/revive/blob/main/crates/runner/src/lib.rs. [44] Technologies, P., 2026. Revive. https://github.com/paritytech/ revive. [45] Tian, Z., Wang, F., Chen, Y., Chen, L., 2024. Differential testing solidity compiler through deep contract manipulation and mutation . [46] Tu, H., Jiang, H., Zhou, Z., Tang, Y., Ren, Z., Qiao, L., Jiang, L., 2022. Detecting c++ compiler front-end bugs via grammar mutation and differential testing. IEEE Transactions on Reliability 72, 343–357. [47] Tu, H., Zhou, Z., Jiang, H., Yusuf, I.N.B., Li, Y., Jiang, L., 2024. Isolating compiler bugs by generating effective witness programs with large language models. IEEE Transactions on Software Engineering . [48] Wang, J., Chen, B., Wei, L., Liu, Y., 2019. Superion: Grammar-aware greybox fuzzing, in: 2019 IEEE/ACM 41st International Conference on Software Engineering (ICSE), IEEE. pp. 724–735. [49] Wang, J., Zhang, Z., Liu, S., Du, X., Chen, J., 2023a. {FuzzJIT}:{Oracle-Enhanced} fuzzing for {JavaScript} engine {JIT} compiler, in: 32nd USENIX Security Symposium (USENIX Security 23), pp. 1865–1882. [50] Wang, Q., Jung, R., 2024. Rustlantis: Randomized differential testing of the rust compiler. Proceedings of the ACM on Programming Languages 8, 1955–1981. [51] Wang, W., Benea, A., Ivancic, F., 2023b. Zero-config fuzzing for microservices, in: 2023 38th IEEE/ACM International Conference on Automated Software Engineering (ASE), IEEE. pp. 1840–1845. [52] Xu, H., Ma, W., Zhou, T., Zhao, Y., Chen, K., Hu, Q., Liu, Y., Wang, H., 2024. A code knowledge graph-enhanced system for llm-based fuzz driver generation. arXiv preprint arXiv:2411.11532 . [53] Yang, C., Deng, Y., Lu, R., Yao, J., Liu, J., Jabbarvand, R., Zhang, L., 2023. White-box compiler fuzzing empowered by large language models. arXiv preprint arXiv:2310.15991 . [54] Zhang, K., Wang, S., Han, J., Zhu, X., Li, X., Wang, S., Wen, S., 2025. Your fix is my exploit: Enabling comprehensive dl library api fuzzing with large language models. arXiv preprint arXiv:2501.04312 . [55] ZKsync, 2026. Differences between zksolc and solc. https:// docs.zksync.io/zksync-protocol/differences/evm-instructions.

Bowei Su et al.: Preprint submitted to Elsevier

Page 15 of 15

Record · ID 175346 · SHA-256 63d70e84f742a6c5
Retrieved via Conceptio — every document is proof-bundled with source, license, and retrieval metadata.