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

# EIP-712 Permit Signature

> Sign a Permit2 or ERC-2612 permit using EIP-712 typed data

Sign a Permit2 or ERC-2612 permit using EIP-712 typed data

```typescript theme={null}
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';
import * as Keccak256 from '@tevm/voltaire/Keccak256';
import * as Hex from '@tevm/voltaire/Hex';
import * as Hash from '@tevm/voltaire/Hash';

// Private key (32 bytes)
const privateKey = Hex.toBytes(
	"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
);

// EIP-712 Domain Separator
const domain = {
	name: "Uniswap V2",
	version: "1",
	chainId: 1n,
	verifyingContract: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
};

// Permit type hash: keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
const PERMIT_TYPEHASH = Hash.keccak256String(
	"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);

// EIP-712 domain type hash
const EIP712_DOMAIN_TYPEHASH = Hash.keccak256String(
	"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);

// Permit message data
const permit = {
	owner: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
	spender: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
	value: 1000000000000000000n, // 1 token (18 decimals)
	nonce: 0n,
	deadline: 1893456000n, // Far future timestamp
};

// Helper: ABI encode an address (20 bytes -> 32 bytes, left-padded)
function encodeAddress(addr: string): Uint8Array {
	const bytes = Hex.toBytes(addr);
	const padded = new Uint8Array(32);
	padded.set(bytes, 12); // Left-pad to 32 bytes
	return padded;
}

// Helper: ABI encode a uint256
function encodeUint256(value: bigint): Uint8Array {
	const bytes = new Uint8Array(32);
	let v = value;
	for (let i = 31; i >= 0; i--) {
		bytes[i] = Number(v & 0xffn);
		v >>= 8n;
	}
	return bytes;
}

// Helper: Concatenate Uint8Arrays
function concat(...arrays: Uint8Array[]): Uint8Array {
	const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
	const result = new Uint8Array(totalLength);
	let offset = 0;
	for (const arr of arrays) {
		result.set(arr, offset);
		offset += arr.length;
	}
	return result;
}

// Step 1: Hash the domain separator
// domainSeparator = keccak256(abi.encode(
//   EIP712_DOMAIN_TYPEHASH,
//   keccak256(name),
//   keccak256(version),
//   chainId,
//   verifyingContract
// ))
const domainSeparator = Keccak256.hash(concat(
	EIP712_DOMAIN_TYPEHASH,
	Hash.keccak256String(domain.name),
	Hash.keccak256String(domain.version),
	encodeUint256(domain.chainId),
	encodeAddress(domain.verifyingContract),
));

// Step 2: Hash the struct data
// structHash = keccak256(abi.encode(
//   PERMIT_TYPEHASH, owner, spender, value, nonce, deadline
// ))
const structHash = Keccak256.hash(concat(
	PERMIT_TYPEHASH,
	encodeAddress(permit.owner),
	encodeAddress(permit.spender),
	encodeUint256(permit.value),
	encodeUint256(permit.nonce),
	encodeUint256(permit.deadline),
));

// Step 3: Create EIP-712 signing hash
// digest = keccak256("\x19\x01" + domainSeparator + structHash)
const prefix = new Uint8Array([0x19, 0x01]);
const digest = Keccak256.hash(concat(prefix, domainSeparator, structHash));

// Step 4: Sign the digest
const signature = Secp256k1.signHash(digest, privateKey);

// Step 5: Extract r, s, v components
const r = Hex.fromBytes(signature.r);
const s = Hex.fromBytes(signature.s);
const v = signature.v;

// Ready for permit() call: token.permit(owner, spender, value, deadline, v, r, s)
```

<Tip>
  This is a fully executable example. View the complete source with test assertions at [`examples/signing/eip712-permit.ts`](https://github.com/evmts/voltaire/blob/main/examples/signing/eip712-permit.ts).
</Tip>

## EIP-712 Structure

EIP-712 defines a standard for typed structured data hashing and signing:

1. **Domain Separator** - Uniquely identifies the application (contract name, version, chain, address)
2. **Type Hash** - keccak256 of the type string (e.g., `"Permit(address owner,...)"`)
3. **Struct Hash** - keccak256 of the encoded struct data with type hash
4. **Digest** - `keccak256("\x19\x01" || domainSeparator || structHash)`

The `\x19\x01` prefix prevents collision with `eth_sign` messages.

## Related

* [Verify Signature](/examples/signing/verify-signature)
* [Personal Sign](/examples/signing/personal-sign)
* [API Reference](/primitives)
