# 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>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://anubis-network.gitbook.io/anubis-network/evm-compatibility-layer-and-precompiled-contracts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
