---
id: TIP-1020
title: Signature Verification Precompile
description: A precompile for verifying Tempo signatures onchain.
authors: Jake Moxey (@jxom), Tanishk Goyal (@legion2002), Howy (@howydev)
status: Scheduled
related: Tempo Transaction Spec
protocolVersion: T3
---

# TIP-1020: Signature Verification Precompile

## Abstract

This TIP introduces a signature verification precompile that enables contracts to verify Tempo signature types (secp256k1, P256, WebAuthn) without relying on custom verifier contracts.

## Motivation

Tempo supports multiple signature schemes beyond standard secp256k1. Currently, contracts cannot verify Tempo signatures onchain without implementing custom verification logic for each signature type.

Additionally, since smart contracts have to statically bind their verification logic at deployment time, developers cannot maintain forward compatibility with future Tempo account signature schemes introduced after deployment without making their contracts upgradeable. This precompile serves as a stable interface that smart contracts can use to maintain forward compatibility with future Tempo account types and signature schemes.

## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

### Precompile Address

```
0x5165300000000000000000000000000000000000
```

### Interface

```solidity
interface ISignatureVerifier {
    error InvalidFormat();
    error InvalidSignature();

    /// @notice Recovers the signer of a Tempo signature (secp256k1, P256, WebAuthn).
    /// @param hash The message hash that was signed
    /// @param signature The encoded signature (see Tempo Transaction spec for formats)
    /// @return Address of the signer if valid, reverts otherwise
    function recover(bytes32 hash, bytes calldata signature) external view returns (address signer);

    /// @notice Verifies a signer against a Tempo signature (secp256k1, P256, WebAuthn).
    /// @param signer The input address verified against the recovered signer
    /// @param hash The message hash that was signed
    /// @param signature The encoded signature (see Tempo Transaction spec for formats)
    /// @return True if the input address signed, false otherwise. Reverts on invalid signatures.
    function verify(address signer, bytes32 hash, bytes calldata signature) external view returns (bool);
}
```

### Signature Encoding

Signatures MUST be encoded using the same format as [Tempo Transaction signatures](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types):

| Type | Format | Length |
|------|--------|--------|
| secp256k1 | `r \|\| s \|\| v` | 65 bytes |
| P256 | `0x01 \|\| r \|\| s \|\| x \|\| y \|\| prehash` | 130 bytes |
| WebAuthn | `0x02 \|\| webauthn_data \|\| r \|\| s \|\| x \|\| y` | 129–2049 bytes |

### Verification Logic

The precompile MUST use the same verification logic as Tempo transaction signature validation. See the [Tempo Transaction Signature Validation spec](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-validation) for details.

#### Keychain Signature Rejection

The precompile MUST reject signatures with a Keychain type prefix (`0x03` or `0x04`). Keychain signatures are multi-step, stateful verification flows that cannot be reduced to a single pure cryptographic check. Thus, if a Keychain prefix is detected, the precompile MUST revert with `InvalidFormat()`.

Contracts that need to verify Keychain-based signatures can do so by composing this precompile with the AccountKeychain precompile: first, use the AccountKeychain precompile to resolve and validate the access key for the account, then use this precompile to verify the inner signature against the resolved key. This two-step pattern separates key management (stateful, account-scoped) from cryptographic verification (stateless, type-scoped), allowing each precompile to remain single-purpose.

### Calldata Limits

The precompile MUST enforce strict size limits on the `signature` argument **before** any decoding or copying occurs. If the signature exceeds the limit for its type, the precompile MUST revert with `InvalidFormat()`.

| Type | Exact / Max Length |
|------|-------------------|
| secp256k1 | exactly 65 bytes |
| P256 | exactly 130 bytes |
| WebAuthn | 129–2049 bytes |

### Gas Costs

The precompile MUST charge gas and verify sufficient gas is available **before** performing any cryptographic verification. The precompile MUST revert with out-of-gas if the call has insufficient gas for the signature type.

All calls pay the standard Tempo precompile calldata cost of 6 gas per 32-byte word (rounded up) on the full ABI-encoded input, consistent with all other Tempo precompiles. The verification gas per signature type is in-line with the [Tempo Transaction Signature Gas Schedule](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-verification-gas-schedule):

| Type | Verification Gas |
|------|-----------------|
| secp256k1 | 3,000 |
| P256 | 8,000 |
| WebAuthn | 8,000 |

Total gas = `input_cost(calldata_len)` + verification gas for the signature type.

## Compatibility

This TIP is **additive**. It introduces a new precompile at `0x5165300000000000000000000000000000000000` and does **not** modify existing EVM opcodes, transaction formats, or any existing Ethereum precompiles.

### Backward Compatibility

#### Ethereum `ecrecover` (`0x01`)

This TIP does not modify `ecrecover` or any existing Ethereum precompile. `ecrecover` remains the standard tool for Ethereum-style secp256k1 address recovery.

#### Developer-Facing Differences vs. `ecrecover`

Solidity developers commonly use `ecrecover(hash, v, r, s)` to recover an address and then compare it to an expected signer. The Tempo signature verification precompile follows the same recover-and-return pattern but differs in one key way: `ecrecover` returns `address(0)` on invalid input or failed recovery, but the TIP-1020 precompile **reverts** on invalid signatures. Contracts that want non-reverting behavior SHOULD wrap calls using `try/catch` (high-level) or `staticcall` (low-level) and treat failure as "invalid signature".

#### `v` Value Normalization

For secp256k1 signatures, the precompile normalizes the recovery identifier `v`: both Ethereum-style values (`27`, `28`) and raw values (`0`, `1`) are accepted. This is intentional — `recover()` is designed to be as close to a drop-in replacement for `ecrecover` as possible, so it accepts the same `v` values. This differs from TIP-1004 (`permit()`), which requires `v ∈ {27, 28}` and reverts on `0` or `1`.

#### Existing secp256k1 Signature Payloads

For backwards compatibility, secp256k1 signatures are encoded as **65 bytes `r || s || v` with no type prefix**. Callers who already produce 65-byte secp256k1 signatures can reuse them directly as the `signature` argument to this precompile.

### Forward Compatibility

It is expected that this precompile will be updated when other account types are introduced to maintain forward compatibility with Tempo accounts.

## Invariants

| ID | Invariant | Description |
|----|-----------|-------------|
| **SV1** | Transaction-equivalent verification | For any signature type supported by a given function, the precompile MUST use the same cryptographic verification rules as Tempo transaction signature validation. |
| **SV2** | P256 and ECDSA signature malleability resistance | P256 and ECDSA signatures MUST satisfy the low-s requirement (`s <= n/2`). Signatures with high-s values MUST be rejected. |
| **SV3** | Signature size enforcement | The precompile MUST enforce per-type size limits (65 bytes secp256k1, 130 bytes P256, 129–2049 bytes WebAuthn) before any decoding or copying, preventing out-of-bounds reads and pathological resource usage. |
| **SV4** | Revert on failure | On any invalid signature, invalid encoding, or unsupported type, the precompile MUST revert. |
| **SV5** | Gas schedule consistency | Gas charged MUST follow the gas schedule listed above. |
| **SV6** | Signature type disambiguation | Exactly 65 bytes MUST be interpreted as secp256k1 (no prefix). Any non-65-byte signature MUST be interpreted using the leading type byte. Unknown type identifiers MUST revert. |
| **SV7** | Keychain signature rejection | Signatures with a Keychain type prefix (`0x03` or `0x04`) MUST be rejected. Keychain verification is achieved by composing the AccountKeychain precompile (key resolution) with this precompile (inner signature verification). |
