# secp256r1 & Schnorr Precompiles

The secp256r1 and Schnorr precompiles are enabled with the [Tangerine](https://www.blog.citrea.xyz/tangerine-upgrade-bitvm-activation-on-clementine/) upgrade of Citrea. In this document we will go over the details of these precompiles and how to use them.

### secp256r1 Curve Support ([RIP 7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md))

RIP 7212 is an improvement proposal that introduces a precompile support for the secp256r1 curve. In Citrea, like other EVM rollups, this precompile is available at address `0x0000000000000000000000000000000000000100`.

This precompile enables a whole new set of applications and use cases, such as:

* **Hardware & Biometric Authentication**: Native support of secp256r1 allows smart contracts to directly validate the signatures from secure enclaves and passkey devices, such as Apple's [Secure Enclave](https://support.apple.com/guide/security/secure-enclave-sec59b0b31ff/web), Android's [Keystore](https://developer.android.com/privacy-and-security/keystore), Yubikeys, and WebAuthn authenticators.
* **Account Abstraction & Smart Wallets**: Combining features above with account abstraction & smart wallets, self-custodial platforms can be built much more easily and efficiently, such as [Tanari](https://www.tanari.io/). It also improves the security and the UX perspective of applications massively.
* **Gas-efficient signature verification**: With this precompile, secp256r1 signature verification comes down to `3450` gas, which is significantly cheaper than any other existing smart contract verification method.

#### Technical Details

The precompile accepts a 160-byte input, which is a concatenation of:

1. `Message Hash (32 bytes)`: The 32-byte hash of the message that was signed.
2. `Signature r value (32 bytes)`: The r component of the ECDSA signature.
3. `Signature s value (32 bytes)`: The s component of the ECDSA signature.
4. `Public Key X-coordinate (32 bytes)`: The X-coordinate of the uncompressed secp256r1 public key.
5. `Public Key Y-coordinate (32 bytes)`: The Y-coordinate of the uncompressed secp256r1 public key.

Upon successful verification, the precompile returns a 32-byte value `0x00...01`. On failure (e.g., invalid signature, malformed input), it returns empty bytes: `0x`.

### Example Smart Contract

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

/// @title secp256r1 Precompile Example
/// @notice Calls the secp256r1 precompile to verify secp256r1 ECDSA signatures
contract P256R1VerifyCaller {
    address constant P256R1_VERIFY_PRECOMPILE = 0x0000000000000000000000000000000000000100;

    /**
     * @notice Verifies a secp256r1 (RIP-7212) ECDSA signature.
     * @dev All inputs must be 32-byte big-endian values.
     * @param messageHash 32-byte message hash
     * @param r 32-byte signature r value
     * @param s 32-byte signature s value
     * @param pubKeyX 32-byte public key X coordinate
     * @param pubKeyY 32-byte public key Y coordinate 
     * @return isValid True if signature is valid, false otherwise.
     */
    function callP256R1Verify(
        bytes32 messageHash,
        bytes32 r,
        bytes32 s,
        bytes32 pubKeyX,
        bytes32 pubKeyY
    ) external view returns (bool isValid) {
        // Concatenate inputs in correct order: hash | r | s | pubKeyX | pubKeyY
        bytes memory input = abi.encodePacked(
            messageHash,
            r,
            s,
            pubKeyX,
            pubKeyY
        );
        (bool ok, bytes memory output) = P256R1_VERIFY_PRECOMPILE.staticcall(input);
        // 32-byte return, last byte == 0x01 means success
        return ok && output.length == 32 && output[31] == 0x01;
    }
}
```

***

### Schnorr Signature Verification

Schnorr signature is a Bitcoin-native, linear, and aggregation-friendly signature scheme. It is a key component of Bitcoin's Taproot upgrade ([BIP 340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)) and also offers advantages for multi-signature schemes like [MuSig2](https://bitcoinops.org/en/topics/musig/). In Citrea, this precompile is available at the address `0x0000000000000000000000000000000000000200`.

Schnorr precompile enables an interesting set of developments, such as:

* **Scriptless cross-chain atomic swaps**: With Schnorr adaptor signatures it is possible to build BTC <> cBTC atomic swaps without HTLCs or any other third-party custody.
* **Bitcoin-aware oracles & bridges**: A smart contract on Citrea can now prove that a Taproot key signed some data without any external verifiers.

#### Technical Details

The precompile accepts a 128-byte input, concatenated as follows:

1. `Public Key X-coordinate (32 bytes)`: The 32-byte x-coordinate of the Schnorr public key. The y-coordinate is implicitly assumed to be even as per BIP340.
2. `Message Hash (32 bytes)`: The 32-byte hash of the message that was signed.
3. `Signature (64 bytes)`: The 64-byte Schnorr signature (typically r and s components, each 32 bytes).

If the signature is valid for the given message hash and public key, the precompile returns a 32-byte value `0x00...01`. If verification fails, it returns empty bytes: `0x`.

Base gas cost of Schnorr precompile is set to `4600`.

### Example Smart Contract

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

/// @title Schnorr Precompile Example
/// @notice Calls the Schnorr precompile to verify BIP340 secp256k1 signatures
contract SchnorrVerifyCaller {
    address constant SCHNORR_VERIFY_PRECOMPILE = 0x0000000000000000000000000000000000000200;

    /**
     * @notice Verifies a BIP340 Schnorr signature.
     * @dev All inputs must be big-endian byte sequences.
     * @param pubKeyX 32-byte public key X coordinate (big-endian, Y is implicitly even per BIP340)
     * @param messageHash 32-byte hash of the signed message
     * @param signature 64-byte Schnorr signature (r || s), both 32-byte values concatenated
     * @return isValid True if signature is valid, false otherwise.
     */
    function schnorrVerify(
        bytes32 pubKeyX,
        bytes32 messageHash,
        bytes calldata signature // must be 64 bytes
    ) external view returns (bool isValid) {
        require(signature.length == 64, "Invalid signature length");
        // Concatenate inputs in correct order: pubKeyX | messageHash | signature
        bytes memory input = abi.encodePacked(
            pubKeyX,
            messageHash,
            signature
        );
        (bool ok, bytes memory output) = SCHNORR_VERIFY_PRECOMPILE.staticcall(input);
        // 32-byte return, last byte == 0x01 means success
        return ok && output.length == 32 && output[31] == 0x01;
    }
}
```


---

# 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://docs.citrea.xyz/developer-documentation/schnorr-secp256r1.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.
