> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-evm-cookbook.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# P256 Precompile

> Use the P256 precompile to verify secp256r1/P-256 signatures directly from EVM smart contracts, enabling efficient passkey-based authentication and signature verification.

**Address:** `0x0000000000000000000000000000000000001011`

This precompile implements [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md), which defines a precompiled contract to verify signatures on the `secp256r1` or `P-256` elliptic curve.

## Overview

The P256 precompile verifies signatures on the `secp256r1` curve efficiently. Many modern security systems use this curve, including:

* Apple's Secure Enclave
* WebAuthn and FIDO2
* Android Keychain
* Various hardware security modules (HSMs)
* Passkeys

This precompile implementation is significantly more gas efficient than Solidity-based implementations (up to 60x).

## Interface

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

address constant P256VERIFY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001011;

IP256VERIFY constant P256VERIFY_CONTRACT = IP256VERIFY(P256VERIFY_PRECOMPILE_ADDRESS);

interface IP256VERIFY {
    function verify(
        bytes memory signature
    ) external view returns (bytes memory response);
}
```

## Implementation library

This is a complete implementation of the P256 library, a convenient wrapper around the precompile:

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./ECDSA.sol";
import "./P256Verify.sol";

/// @title P256
/// @author klkvr <https://github.com/klkvr>
/// @author jxom <https://github.com/jxom>
/// @notice Wrapper function to abstract low level details of call to the P256
///         signature verification precompile as defined in EIP-7212, see
///         <https://eips.ethereum.org/EIPS/eip-7212>.
library P256 {
    /// @notice P256VERIFY operation
    /// @param digest 32 bytes of the signed data hash
    /// @param signature Signature of the signer
    /// @param publicKey Public key of the signer
    /// @return success Represents if the operation was successful
    function verify(bytes32 digest, ECDSA.Signature memory signature, ECDSA.PublicKey memory publicKey)
        internal
        view
        returns (bool)
    {
        bytes memory input = abi.encode(digest, signature.r, signature.s, publicKey.x, publicKey.y);
        // On invalid signatures the precompile returns no data, which would
        // make a high-level interface call revert when decoding the `bytes`
        // return value. Use a low-level staticcall and treat empty return
        // data as "invalid signature" instead.
        (bool success, bytes memory output) = P256VERIFY_PRECOMPILE_ADDRESS.staticcall(
            abi.encodeWithSelector(IP256VERIFY.verify.selector, input)
        );

        return success && output.length > 0;
    }
}
```

## Basic usage

### Using the P256 library

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./P256.sol";
import "./ECDSA.sol";

contract P256Example {
    using P256 for bytes32;

    function verifySignature(
        bytes32 messageHash,
        ECDSA.Signature memory signature,
        ECDSA.PublicKey memory publicKey
    ) public view returns (bool) {
        return P256.verify(messageHash, signature, publicKey);
    }
}
```

### Direct precompile usage

For more control, you can call the precompile directly:

```solidity theme={null}
pragma solidity ^0.8.0;

interface IP256Verify {
    function verify(bytes calldata input) external view returns (bytes memory response);
}

contract P256Verifier {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    function verifySignature(
        bytes32 messageHash,
        bytes32 r,
        bytes32 s,
        bytes32 publicKeyX,
        bytes32 publicKeyY
    ) external view returns (bool) {
        bytes memory input = abi.encodePacked(
            messageHash,
            r,
            s,
            publicKeyX,
            publicKeyY
        );

        // Success returns a non-empty (ABI-encoded 32-byte) value; invalid
        // signatures return no data, which would revert a high-level call.
        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );
        return ok && output.length > 0;
    }
}
```

## Signature format

Signature verification requires these components:

* `digest`: 32 bytes of the signed data hash
* `signature`: Contains the r and s components of the signature
* `publicKey`: Contains the x and y coordinates of the public key

The precompile expects these components encoded in this format:

* First 32 bytes: message hash
* Next 32 bytes: `r` component of the signature
* Next 32 bytes: `s` component of the signature
* Next 32 bytes: `x` coordinate of the public key
* Next 32 bytes: `y` coordinate of the public key

Total length: 160 bytes

## Gas costs

The precompile is much more gas efficient than Solidity implementations. The exact gas cost per byte of verified data is set to `GasCostPerByte = 300`. The results are:

* **Total cost**: 300 × 160 = **48,000 gas** per verification
* **Efficiency**: Up to 60x more efficient than pure Solidity implementations

## Real-world use cases

### WebAuthn/passkeys authentication

```solidity theme={null}
pragma solidity ^0.8.0;

contract WebAuthnAuth {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    struct PublicKey {
        bytes32 x;
        bytes32 y;
    }

    mapping(address => PublicKey) public userKeys;

    function registerPasskey(
        bytes32 keyX,
        bytes32 keyY
    ) external {
        userKeys[msg.sender] = PublicKey(keyX, keyY);
    }

    function authenticateWithPasskey(
        bytes32 challengeHash,
        bytes32 r,
        bytes32 s
    ) external view returns (bool) {
        PublicKey memory userKey = userKeys[msg.sender];
        require(userKey.x != 0 && userKey.y != 0, "No passkey registered");

        bytes memory input = abi.encodePacked(
            challengeHash,
            r,
            s,
            userKey.x,
            userKey.y
        );

        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );
        return ok && output.length > 0;
    }
}
```

### Apple Secure Enclave integration

```solidity theme={null}
contract SecureEnclaveAuth {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    event SecureOperation(address indexed user, bytes32 indexed operationHash);

    function executeSecureOperation(
        bytes32 operationHash,
        bytes32 r,
        bytes32 s,
        bytes32 enclaveKeyX,
        bytes32 enclaveKeyY
    ) external {
        require(
            verifyEnclaveSignature(operationHash, r, s, enclaveKeyX, enclaveKeyY),
            "Invalid Secure Enclave signature"
        );

        emit SecureOperation(msg.sender, operationHash);
    }

    function verifyEnclaveSignature(
        bytes32 hash,
        bytes32 r,
        bytes32 s,
        bytes32 x,
        bytes32 y
    ) internal view returns (bool) {
        bytes memory input = abi.encodePacked(hash, r, s, x, y);
        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );
        return ok && output.length > 0;
    }
}
```

### Multi-signature with hardware keys

```solidity theme={null}
contract HardwareMultiSig {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    struct Signature {
        bytes32 r;
        bytes32 s;
    }

    struct PublicKey {
        bytes32 x;
        bytes32 y;
    }

    function verifyMultipleHardwareKeys(
        bytes32 messageHash,
        Signature[] calldata signatures,
        PublicKey[] calldata publicKeys,
        uint256 threshold
    ) external view returns (bool) {
        require(signatures.length == publicKeys.length, "Mismatched arrays");
        require(threshold <= signatures.length, "Invalid threshold");

        uint256 validSignatures = 0;

        for (uint i = 0; i < signatures.length; i++) {
            bytes memory input = abi.encodePacked(
                messageHash,
                signatures[i].r,
                signatures[i].s,
                publicKeys[i].x,
                publicKeys[i].y
            );

            (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
                abi.encodeWithSelector(IP256Verify.verify.selector, input)
            );
            if (ok && output.length > 0) {
                validSignatures++;
            }
        }

        return validSignatures >= threshold;
    }
}
```

## Security considerations

<Warning>Always validate public keys and signature components before verification. This prevents invalid curve point attacks.</Warning>

### Public key validation

```solidity theme={null}
function isValidPublicKey(bytes32 x, bytes32 y) internal pure returns (bool) {
    // Ensure point is not at infinity
    if (x == 0 && y == 0) return false;

    // Additional validation should be performed for production use
    // The precompile will reject invalid curve points
    return true;
}
```

### Signature malleability

P256 signatures can be malleable. If your application requires unique signatures, implement more checks:

```solidity theme={null}
function isLowS(bytes32 s) internal pure returns (bool) {
    // Check if s is in the lower half of the curve order (n / 2, where n is
    // the P-256 group order). This prevents signature malleability.
    return uint256(s) <= 0x7FFFFFFF800000007FFFFFFFFFFFFFFFDE737D56D38BCF4279DCE5617E3192A8;
}
```

## JavaScript integration

### Preparing input data

```javascript theme={null}
// Example: Preparing P256 verification input (ethers v6)
function prepareP256Input(messageHash, signature, publicKey) {
  // Ensure all components are 32 bytes
  const hash = ethers.zeroPadValue(messageHash, 32);
  const r = ethers.zeroPadValue(signature.r, 32);
  const s = ethers.zeroPadValue(signature.s, 32);
  const x = ethers.zeroPadValue(publicKey.x, 32);
  const y = ethers.zeroPadValue(publicKey.y, 32);

  // Concatenate all components
  return ethers.concat([hash, r, s, x, y]);
}

// Usage with ethers.js
import { P256_PRECOMPILE_ABI, P256_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';

async function verifyP256Signature(provider, messageHash, signature, publicKey) {
  const precompile = new ethers.Contract(P256_PRECOMPILE_ADDRESS, P256_PRECOMPILE_ABI, provider);
  const input = prepareP256Input(messageHash, signature, publicKey);
  try {
    const result = await precompile.verify(input);
    // Success returns 32 bytes ending in 0x01
    return result === '0x0000000000000000000000000000000000000000000000000000000000000001';
  } catch (err) {
    // Invalid signatures return no data, which ethers cannot decode as `bytes`
    // (BAD_DATA) — treat that as a failed verification.
    return false;
  }
}
```

## Error handling

The P256 precompile returns no data on failure. Because `verify` is declared to return `bytes`, a high-level Solidity interface call reverts when it tries to decode the empty return data. Always use a low-level `staticcall`, as the examples above do. Invalid signatures then resolve to `false` instead of reverting. Common failure cases include:

1. **Invalid input length**: The input must be exactly 160 bytes.
2. **Invalid public key**: The point is not on the P256 curve.
3. **Invalid signature**: The r or s values are out of the valid range.
4. **Verification failure**: The signature does not match the message and public key.

```solidity theme={null}
function safeVerifyP256(
    bytes32 messageHash,
    bytes32 r,
    bytes32 s,
    bytes32 publicKeyX,
    bytes32 publicKeyY
) internal view returns (bool success, string memory error) {
    // Validate inputs
    if (!isValidPublicKey(publicKeyX, publicKeyY)) {
        return (false, "Invalid public key");
    }

    if (r == 0 || s == 0) {
        return (false, "Invalid signature components");
    }

    // Prepare input and verify
    bytes memory input = abi.encodePacked(
        messageHash, r, s, publicKeyX, publicKeyY
    );

    (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
        abi.encodeWithSelector(IP256Verify.verify.selector, input)
    );
    bool verified = ok && output.length > 0;
    return (verified, verified ? "" : "Verification failed");
}
```

## Testing

### Unit tests

```solidity theme={null}
contract P256Test {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    function testValidSignature() public {
        // Test with known valid P256 signature
        bytes32 messageHash = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef;
        // Use actual test vectors for production tests
        bytes32 r = 0x...; // Valid r component
        bytes32 s = 0x...; // Valid s component
        bytes32 x = 0x...; // Valid public key x
        bytes32 y = 0x...; // Valid public key y

        bytes memory input = abi.encodePacked(messageHash, r, s, x, y);
        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );

        assert(ok && output.length > 0);
    }
}
```

## Performance considerations

* **Gas efficiency**: 48,000 gas per verification, compared to 2M gas or more for Solidity implementations
* **Batch operations**: Consider batching multiple verifications in a single transaction
* **Caching**: Cache public keys on-chain to reduce calldata for repeated verifications
* **Hardware integration**: Particularly efficient for applications that use hardware-backed keys

<Info>View the complete P256 precompile ABI in the [sei-chain v6.6.1 snapshot](https://github.com/sei-protocol/sei-chain/blob/v6.6.1/precompiles/p256/legacy/v66/abi.json), or browse the [implementation source](https://github.com/sei-protocol/sei-chain/tree/v6.6.1/precompiles/p256).</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.