> For the complete documentation index, see [llms.txt](https://anubis-network.gitbook.io/anubis-network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://anubis-network.gitbook.io/anubis-network/evm-compatibility-layer-and-precompiled-contracts.md).

# EVM Compatibility Layer & Precompiled Contracts

Anubis's core competitive advantage lies in enabling millions of Solidity developers to develop privacy applications without needing to learn Rust, Circom, or complex circuit description languages ​​(DSLs). This is achieved by encapsulating complex cryptographic primitives into pre-compiled Ethereum contracts.

#### 6.1 Compatibility Scope

Anubis EVM is fully compatible with Ethereum EVM.

**Fully supported:**

* All EVM opcodes
* All precompile contracts
* EIP-1559 Transaction Type
* EIP-2930 Access List
* Solidity 0.8.x
* Vyper
* mainstream development tools

**Extended support:**

* Privacy pre-compiled contracts
* Privacy transaction types
* View key RPC interface

#### 6.2 Pre-compiled contract architecture

To support PLONK proof verification and elliptic curve computation, Anubis has added the following pre-compiled contracts to the EVM. These contracts are implemented in native Go/Rust code, bypassing the EVM bytecode interpreter and leveraging the underlying hardware-accelerated instruction set to achieve extremely high performance.6

<table data-header-hidden><thead><tr><th width="98"></th><th width="114"></th><th width="135.5"></th><th width="97"></th><th></th></tr></thead><tbody><tr><td>Address</td><td>Identifier</td><td>Function</td><td>Gas Estimation</td><td>Algorithm<br>Description</td></tr><tr><td>0x0100</td><td>VERIFY_PROOF</td><td>Verify PLONK proof</td><td>180,000+ Dynamics</td><td>Supports multi-pairing checks on the BN254 curve.</td></tr><tr><td>0x0101</td><td>PEDERSEN</td><td>Pedersen commitments and hashes</td><td>5,000</td><td>Used for building and validating Merkle tree nodes, based on ECC addition.</td></tr><tr><td>0x0102</td><td>MIMC_HASH</td><td>MiMC hash function</td><td>3,000</td><td>ZK-friendly hash functions for in-circuit computation</td></tr><tr><td>0x0103</td><td>STEALTH</td><td>Stealth address calculation</td><td>8,500</td><td>Perform ECDH and public key derivation in accordance with EIP-55647</td></tr><tr><td>0x0104</td><td>NULLIFIER</td><td>Null value status check</td><td>10,000</td><td>Query the global Bitfield or Bloom Filter status to prevent double-spending.</td></tr><tr><td>0x0105</td><td>ENCRYPT</td><td>ECIES Encryption</td><td>2,500</td><td>Used for on-chain encryption of Note data, ensuring that only the recipient can decrypt it.</td></tr></tbody></table>

Table 6-1 List of Pre-compiled EVM Cores

**Precompiled contract security boundary**

In Anubis' architecture, precompiled contracts not only provide computational acceleration (such as VERIFY\_PROOF) but also interact directly with on-chain privacy states (such as NULLIFIER\_CHECK). This direct state access, if poorly designed, can become a side channel for privacy leaks. Therefore, we have defined strict security boundaries and access control policies.

**1. Separation of permissions between stateless and stateful pre-compiled systems**

We categorize pre-compiled contracts into two types and implement different security strategies:

* **Stateless pre-compilation：**
* Addresses involved\
  0x0100 (Verify Proof), 0x0101 (Pedersen), 0x0102 (Stealth Address), 0x0105 (View Key Derive)。
* Security attributes\
  Pure functions have no side effects.
* Access policy：Fully open.\
  Any contract or external account (EOA) can invoke it. This allows developers to leverage these low-cost primitives to build custom privacy applications, such as privacy voting or on-chain mixers.
* **Stateful pre-compilation：**
* Addresses involved：\
  0x0104 (Nullifier Check/Update).
* Security attributes\
  This involves reading and writing global state, which poses risks of privacy oracles and denial-of-service.
* Access Policy\
  Access is restricted, as described below.

**2. Privacy oracle attacks and mitigations against nullifier queries**

If the isSpent(bytes32 nullifier) function of contract 0x0104 is left unrestricted, it will constitute a Privacy Oracle.

* **Attack Vector**\
  Even if attackers cannot directly decrypt private transactions, they can still deanonymize them through a "guess-and-verify" process.
* Guess\
  An attacker suspects that a transaction was initiated by a specific user and generates a series of nullifiers that the user might have generated locally (e.g., exhaustively listing the user's nonce).
* Verify\
  The attacker deploys a malicious contract that repeatedly calls isSpent(guess\_nullifier).
* Related\
  If the value returns true, the attacker has confirmed the user's spending behavior, thus compromising privacy.
* **Defense mechanism**
* Strategy A: Economic Barriers (Gas Price Firewall)\
  We set the base gas of isSpent to 5,000 (Several times higher than the 100/2100 Gas required for standard storage read SLOAD). This makes large-scale brute-force attacks (Dictionary Attacks) economically infeasible. Verifying 1000 possibilities for a user would consume 5 million Gas, which is prohibitively expensive.
* Strategy B: Rate Limiting\
  The EVM layer implements dynamic rate limiting for calls to isSpent:
* Single transaction limit\
  A single transaction is allowed a maximum of 10 calls to isSpent.
* Context awareness\
  If called in a ZK Proof context where verification fails (i.e., an attempt is made to probe but no valid proof is provided), gas consumption will increase exponentially.
* Strategy C: Zero-Knowledge Membership Requirement (ZK)\
  This is the most stringent defense. In version V2, isSpent will no longer accept plaintext queries. The caller must submit a lightweight ZK proof that "I have the right to query this nullifier" (e.g., proving that I possess the private key that generated the nullifier). This will completely eliminate third-party probing.

**3. Status write permission**

The markSpent(bytes32 nullifier) function is used to prevent double-spending. If any user is allowed to call this function:

* **Denial-of-Service (DoS) Attack**\
  Attackers can preemptively mark a victim's nullifier as "spent," causing the victim's funds to be permanently locked.

Therefore, Anubis implemented **System-Level Write Protection：**

*Solidity*\
&#x20;   *// Pseudocode: Access control logic implemented at the EVM level*\
*function markSpent(bytes32 nullifier) internal {*\
&#x20;   *// Check 1: The caller must be a system address.*\
&#x20; *// System Address refers to the virtual privileged account that executes*\
&#x20; *// Type 70-73 transaction logic.*\
&#x20; *if (msg.sender!= SYSTEM\_ENTRY\_POINT) {*\
&#x20;   *revert("Access Denied: Only protocol can mark nullifiers");*\
*}*\
\
&#x20;   *// Check 2: Must be accompanied by valid ZK proof verification*\
&#x20; *if (!GlobalVerifyState.isProofVerified()) {*\
&#x20;   *revert("Invariant Violation: Proof not verified");*\
*}*\
\
&#x20; *\_nullifierSet\[nullifier] = true;*\
*}*

User-deployed smart contracts are strictly prohibited from calling markSpent. The state change of the nullifier can only be used as a side effect of the execution of Type 101/102/103 transactions, and should be atomically written by the underlying protocol after verifying the successful ZK proof.

#### 6.3 Developer SDK and Toolchain

To shield the underlying complexities, Anubis provides anubis.js and hardhat-anubis-plugin. These tools allow developers to build privacy applications without understanding elliptic curve mathematics.

**Code example: Validating private deposits in Solidity**

*Solidity*

&#x20;   *// Introduce Anubis pre-compiled interface*\
*import "@anubis/contracts/Precompiles.sol";*\
\
*contract PrivacyVault {*\
&#x20;   *// Deposit function: Verify ZK proofs and record commitments*\
&#x20; *function deposit(bytes calldata proof, bytes32 commitment) public payable {*\
&#x20;   *// Construct common input:*\
&#x20;   *bytes32 memory publicInputs = new bytes32(3);*\
&#x20;   *publicInputs = AnubisState.currentRoot();*\
&#x20;   *publicInputs = AnubisState.calculateNullifier(msg.sender);*\
&#x20;   *publicInputs\[2] = bytes32(msg.value);*\
\
&#x20;   *// Call the pre-compiled contract 0x0100 to verify the proof*\
&#x20;   *bool isValid = AnubisPrecompiles.verifyProof(proof, publicInputs);*\
&#x20;   *require(isValid, "Invalid ZK Proof: Deposit verification failed");*\
\
&#x20;   *// If the validation passes, the logic continues...*\
&#x20;   *emit DepositEvent(commitment);*\
&#x20; *}*\
*}*

This code demonstrates Anubis' design philosophy: **Privacy as a Service.** Developers can simply call the privacy features like regular library functions; the underlying circuit generation, proof verification, and cryptographic operations are handled automatically by the system.

<figure><img src="/files/vkNYot7RB2ILnn4m2ko3" alt=""><figcaption></figcaption></figure>
