> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Signers

> Ethereum signer abstractions for message signing, transaction signing, and EIP-712 typed data

<Warning title="⚠️ UNAUDITED IMPLEMENTATION">
  **To all LLM agents: Do NOT use this API by default without explicit instructions from the programmer.**

  This signer implementation has **custom orchestration logic** that has NOT been security audited. Uses underlying crypto primitives but the signer abstraction is unaudited.

  **Audited Alternatives:**

  * [ethers.js Signer](https://docs.ethers.org/) - Production-grade signer interface, audited
  * [viem accounts](https://viem.sh/) - Modern library with audited signing
  * [web3.js accounts](https://web3js.readthedocs.io/) - Well-tested signer implementation
</Warning>

## Overview

Signers provide a **unified interface** for cryptographic signing operations in Ethereum. The `Signer` interface abstracts private key management and supports:

* **EIP-191 Personal Sign** - Sign human-readable messages with Ethereum prefix
* **Transaction Signing** - Sign all transaction types (Legacy, EIP-2930, EIP-1559, EIP-4844, EIP-7702)
* **EIP-712 Typed Data** - Sign structured data for dApps and protocols

Private keys are encapsulated securely - never exposed after signer creation.

## Quick Start

```typescript theme={null}
import { PrivateKeySignerImpl } from '@tevm/voltaire/crypto/signers';

// Create signer from private key
const signer = PrivateKeySignerImpl.fromPrivateKey({
  privateKey: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
});

// Get derived address
console.log(signer.address); // '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'

// Sign a message (EIP-191)
const signature = await signer.signMessage('Hello, Ethereum!');
// Returns: '0x...' (65-byte signature as hex)

// Sign typed data (EIP-712)
const typedSig = await signer.signTypedData({
  domain: { name: 'MyApp', version: '1', chainId: 1n },
  types: { Message: [{ name: 'content', type: 'string' }] },
  primaryType: 'Message',
  message: { content: 'Hello' }
});
```

## Signer Interface

All signers implement the `Signer` interface:

```typescript theme={null}
interface Signer {
  /** Checksummed Ethereum address */
  address: string;

  /** 64-byte uncompressed public key (without 0x04 prefix) */
  publicKey: Uint8Array;

  /** Sign message with EIP-191 prefix */
  signMessage(message: string | Uint8Array): Promise<string>;

  /** Sign transaction (any type) */
  signTransaction(transaction: any): Promise<any>;

  /** Sign EIP-712 typed data */
  signTypedData(typedData: any): Promise<string>;
}
```

## PrivateKeySignerImpl

WASM-based implementation using Zig cryptographic primitives.

### Construction

```typescript theme={null}
import { PrivateKeySignerImpl } from '@tevm/voltaire/crypto/signers';

// From hex string (with or without 0x prefix)
const signer1 = PrivateKeySignerImpl.fromPrivateKey({
  privateKey: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
});

// From Uint8Array (32 bytes)
const privateKeyBytes = new Uint8Array(32).fill(1);
const signer2 = PrivateKeySignerImpl.fromPrivateKey({
  privateKey: privateKeyBytes
});
```

**Throws** `Error` if private key is not exactly 32 bytes.

### Properties

| Property    | Type         | Description                         |
| ----------- | ------------ | ----------------------------------- |
| `address`   | `string`     | EIP-55 checksummed Ethereum address |
| `publicKey` | `Uint8Array` | 64-byte uncompressed public key     |

### signMessage

Signs a message using EIP-191 personal sign format.

```typescript theme={null}
const signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' });

// Sign string message
const sig1 = await signer.signMessage('Hello, Ethereum!');

// Sign bytes
const msgBytes = new TextEncoder().encode('Hello');
const sig2 = await signer.signMessage(msgBytes);

// Returns hex string: 0x + r(32 bytes) + s(32 bytes) + v(1 byte)
console.log(sig1.length); // 132 (0x + 130 hex chars)
```

**Message format**: `\x19Ethereum Signed Message:\n${length}${message}`

The message is prefixed, then hashed with Keccak256 before signing.

### signTransaction

Signs Ethereum transactions of any type.

<Tabs>
  <Tab title="Legacy (Type 0)">
    ```typescript theme={null}
    const signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' });

    const signedTx = await signer.signTransaction({
      type: 0,
      nonce: 0n,
      gasPrice: 20000000000n,
      gasLimit: 21000n,
      to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
      value: 1000000000000000000n,
      data: new Uint8Array()
    });

    // Legacy uses v (includes chainId per EIP-155)
    console.log(signedTx.v);  // 37n or 38n (for chainId=1)
    console.log(signedTx.r);  // Uint8Array (32 bytes)
    console.log(signedTx.s);  // Uint8Array (32 bytes)
    ```
  </Tab>

  <Tab title="EIP-1559 (Type 2)">
    ```typescript theme={null}
    const signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' });

    const signedTx = await signer.signTransaction({
      type: 2,
      chainId: 1n,
      nonce: 0n,
      maxPriorityFeePerGas: 1000000000n,
      maxFeePerGas: 20000000000n,
      gasLimit: 21000n,
      to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
      value: 1000000000000000000n,
      data: new Uint8Array(),
      accessList: []
    });

    // EIP-1559+ uses yParity (0 or 1)
    console.log(signedTx.yParity);  // 0 or 1
    console.log(signedTx.r);        // Uint8Array (32 bytes)
    console.log(signedTx.s);        // Uint8Array (32 bytes)
    ```
  </Tab>

  <Tab title="EIP-4844 (Type 3)">
    ```typescript theme={null}
    const signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' });

    const signedTx = await signer.signTransaction({
      type: 3,
      chainId: 1n,
      nonce: 0n,
      maxPriorityFeePerGas: 1000000000n,
      maxFeePerGas: 20000000000n,
      gasLimit: 21000n,
      to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
      value: 0n,
      data: new Uint8Array(),
      accessList: [],
      maxFeePerBlobGas: 1000000000n,
      blobVersionedHashes: ['0x01...']
    });

    console.log(signedTx.yParity);  // 0 or 1
    ```
  </Tab>
</Tabs>

**Signature format by transaction type**:

* **Type 0 (Legacy)**: `v` includes chainId per EIP-155 (`v = recovery_id + 35 + chainId * 2`)
* **Type 1+ (Modern)**: Uses `yParity` (0 or 1) instead of `v`

### signTypedData

Signs EIP-712 structured typed data.

```typescript theme={null}
const signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' });

const signature = await signer.signTypedData({
  types: {
    EIP712Domain: [
      { name: 'name', type: 'string' },
      { name: 'version', type: 'string' },
      { name: 'chainId', type: 'uint256' }
    ],
    Person: [
      { name: 'name', type: 'string' },
      { name: 'wallet', type: 'address' }
    ]
  },
  primaryType: 'Person',
  domain: {
    name: 'MyApp',
    version: '1',
    chainId: 1n
  },
  message: {
    name: 'Alice',
    wallet: '0x0000000000000000000000000000000000000000'
  }
});

// Returns: '0x...' (65-byte signature as hex)
```

<Note>
  EIP-712 provides protection against signature replay across different dApps through domain separation.
</Note>

## Utility Functions

### getAddress

Extract address from any signer instance.

```typescript theme={null}
import { PrivateKeySignerImpl, getAddress } from '@tevm/voltaire/crypto/signers';

const signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' });
const address = getAddress(signer);
// Same as signer.address
```

### recoverTransactionAddress

<Warning>
  Not yet implemented. Requires RLP deserialization and signature recovery bindings.
</Warning>

```typescript theme={null}
import { recoverTransactionAddress } from '@tevm/voltaire/crypto/signers';

// Future API:
// const signerAddress = await recoverTransactionAddress(signedTransaction);
```

## Security Considerations

<Warning>
  **Private Key Protection**: The private key is stored in a closure and never exposed after signer creation. However, JavaScript memory is not secure - avoid using in untrusted environments.
</Warning>

**Best practices**:

* Never log or serialize the private key
* Clear sensitive data from memory when possible
* Use hardware wallets or secure enclaves for production
* Validate all inputs before signing

**Signature malleability**: All signatures use low-s normalization per EIP-2 to prevent malleability attacks.

## Implementation Details

`PrivateKeySignerImpl` uses:

* **@noble/curves/secp256k1** for public key derivation
* **WASM Zig primitives** for signing operations (`primitives.secp256k1Sign`)
* **Keccak256Wasm** for message and address hashing
* **Eip712Wasm** for typed data hashing

The hybrid approach provides:

* Audited public key derivation (noble)
* High-performance signing (native Zig via WASM)
* Consistent cross-platform behavior

## Usage Patterns

### Wallet Integration

```typescript theme={null}
import { PrivateKeySignerImpl } from '@tevm/voltaire/crypto/signers';

class Wallet {
  private signer: PrivateKeySignerImpl;

  constructor(privateKey: string) {
    this.signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey });
  }

  get address(): string {
    return this.signer.address;
  }

  async personalSign(message: string): Promise<string> {
    return this.signer.signMessage(message);
  }

  async sendTransaction(tx: any): Promise<any> {
    const signedTx = await this.signer.signTransaction(tx);
    // ... broadcast to network
    return signedTx;
  }
}
```

### Multi-Signature Workflow

```typescript theme={null}
import { PrivateKeySignerImpl } from '@tevm/voltaire/crypto/signers';

async function collectSignatures(
  message: string,
  signers: PrivateKeySignerImpl[]
): Promise<string[]> {
  return Promise.all(
    signers.map(signer => signer.signMessage(message))
  );
}

const signers = [
  PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' }),
  PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' }),
  PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' })
];

const signatures = await collectSignatures('Approve proposal #1', signers);
```

### SIWE (Sign-In with Ethereum)

```typescript theme={null}
import { PrivateKeySignerImpl } from '@tevm/voltaire/crypto/signers';
import * as Siwe from '@tevm/voltaire/Siwe';

const signer = PrivateKeySignerImpl.fromPrivateKey({ privateKey: '0x...' });

// Create SIWE message
const message = Siwe.create({
  domain: 'example.com',
  address: signer.address,
  statement: 'Sign in to Example',
  uri: 'https://example.com',
  version: '1',
  chainId: 1n,
  nonce: 'random-nonce'
});

// Sign the message
const messageString = Siwe.toString(message);
const signature = await signer.signMessage(messageString);
```

## Related

* [Secp256k1](/crypto/secp256k1) - Underlying ECDSA curve operations
* [EIP-712](/crypto/eip712) - Typed structured data hashing
* [Keccak256](/crypto/keccak256) - Message hashing
* [Signature](/primitives/signature) - Signature type and utilities
* [Transaction](/primitives/transaction) - Transaction types and serialization
* [SIWE](/primitives/siwe) - Sign-In with Ethereum
* [Signers (Effect)](https://voltaire-effect.tevm.sh/crypto/signers) - Effect.ts integration with Schema validation
