This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
Overview
Address: 0x0000000000000000000000000000000000000005
Introduced: Byzantium (EIP-198)
EIP: EIP-198, EIP-2565
The ModExp precompile computes modular exponentiation: (base^exponent) mod modulus. This enables efficient RSA signature verification, Fermat primality testing, and other advanced cryptographic operations in smart contracts.
EIP-198 introduced ModExp in Byzantium. EIP-2565 (Berlin) reduced gas costs to make RSA verification practical.
Gas Cost
Complex formula that varies by hardfork:
Pre-Berlin: max(200, complexity * iteration_count / GQUADDIVISOR)
Berlin+: max(200, complexity * iteration_count / GQUADDIVISOR_v2)
Where:
complexity = mult_complexity * max(length(base), length(modulus))
mult_complexity = (max(length(base), length(modulus)) / 8)^2 if max > 64, else mult_complexity = max(length(base), length(modulus))^2 / 4
iteration_count = max(exponent_bitlength - 1, 1) adjusted for exponent head
- Minimum gas:
200
The exact calculation is in src/crypto/ModExp/calculateGas
Examples:
- Small inputs (1-byte each): ~200 gas
- 256-byte RSA (2048-bit): ~50,000+ gas
- 512-byte RSA (4096-bit): ~200,000+ gas
Offset | Length | Description
-------|--------|-------------
0 | 32 | base_length (big-endian u256)
32 | 32 | exponent_length (big-endian u256)
64 | 32 | modulus_length (big-endian u256)
96 | base_length | base value (big-endian)
96+base_length | exponent_length | exponent value (big-endian)
96+base_length+exponent_length | modulus_length | modulus value (big-endian)
Minimum input length: 96 bytes (length headers only)
Output length equals modulus_length specified in input.
Offset | Length | Description
-------|--------|-------------
0 | modulus_length | (base^exponent mod modulus) as big-endian
Returns empty output if modulus_length = 0.
Usage Example
import { execute, PrecompileAddress } from '@tevm/voltaire/precompiles';
import { Hardfork } from '@tevm/voltaire/primitives/Hardfork';
import * as Hex from '@tevm/voltaire/Hex';
// Compute 2^3 mod 5 = 8 mod 5 = 3
const input = Hex(
'0x' +
'0000000000000000000000000000000000000000000000000000000000000001' + // base_length = 1
'0000000000000000000000000000000000000000000000000000000000000001' + // exponent_length = 1
'0000000000000000000000000000000000000000000000000000000000000001' + // modulus_length = 1
'02' + // base = 2
'03' + // exponent = 3
'05' // modulus = 5
);
const result = execute(
PrecompileAddress.MODEXP,
input,
100000n,
Hardfork.CANCUN
);
if (result.success) {
console.log('Result:', result.output[0]); // 3
console.log('Gas used:', result.gasUsed);
} else {
console.error('Error:', result.error);
}
Error Conditions
- Input length < 96 bytes
- Out of gas (gas cost depends on input sizes)
- Modulus = 0 (returns error)
- Integer overflow in length values
Use Cases
- RSA signature verification: Verify RSA-2048, RSA-4096 signatures on-chain
- Zero-knowledge proofs: Perform modular arithmetic for zkSNARKs
- Cryptographic protocols: Diffie-Hellman key exchange, ElGamal encryption
- Primality testing: Fermat and Miller-Rabin primality tests
- Number theory: Modular inverses, Chinese remainder theorem
Implementation Details
- Zig: Uses multi-precision arithmetic from ModExp crypto module
- TypeScript: BigInt-based modular exponentiation with square-and-multiply
- Integration: Depends on ModExp.calculateGas for hardfork-specific gas calculation
- Optimization: Binary exponentiation (square-and-multiply algorithm)
RSA Verification Example
import * as Hex from '@tevm/voltaire/Hex';
// RSA-2048 verification (256-byte values)
const baseLen = 256;
const expLen = 3; // Common public exponent: 65537
const modLen = 256;
// Example RSA-2048 input (signature verification)
const base = Hex('0x' + 'ab'.repeat(256)); // Signature (256 bytes)
const exponent = Hex('0x010001'); // Public exponent 65537 (3 bytes)
const modulus = Hex('0x' + 'cd'.repeat(256)); // RSA modulus (256 bytes)
const input = Hex(
'0x' +
'0000000000000000000000000000000000000000000000000000000000000100' + // base_length = 256
'0000000000000000000000000000000000000000000000000000000000000003' + // exponent_length = 3
'0000000000000000000000000000000000000000000000000000000000000100' + // modulus_length = 256
Hex.toHex(base).slice(2) +
Hex.toHex(exponent).slice(2) +
Hex.toHex(modulus).slice(2)
);
const result = execute(
PrecompileAddress.MODEXP,
input,
1000000n, // RSA needs significant gas
Hardfork.CANCUN
);
// result.output should equal expected message hash
Gas Cost Reduction (EIP-2565)
Berlin hard fork (EIP-2565) reduced gas costs significantly:
| Input Size | Pre-Berlin | Berlin | Reduction |
|---|
| 2048-bit RSA | ~300,000 | ~50,000 | 83% |
| 4096-bit RSA | ~1,200,000 | ~200,000 | 83% |
This made RSA verification practical for many use cases.
Edge Cases
- Zero exponent: Returns 1 (any number to power 0 is 1)
- Modulus = 1: Returns 0 (anything mod 1 is 0)
- Base > modulus: Automatically reduced mod modulus
- Truncated input: Missing bytes treated as zero
- Zero modulus: Returns error (division by zero)
Test Vectors
import * as Hex from '@tevm/voltaire/Hex';
// Test 1: 2^3 mod 5 = 3
const input1 = Hex(
'0x' +
'0000000000000000000000000000000000000000000000000000000000000001' + // base_len = 1
'0000000000000000000000000000000000000000000000000000000000000001' + // exp_len = 1
'0000000000000000000000000000000000000000000000000000000000000001' + // mod_len = 1
'02' + '03' + '05' // base=2, exp=3, mod=5
);
// Expected: 0x03
// Test 2: 3^1 mod 5 = 3
const input2 = Hex(
'0x' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'03' + '01' + '05' // base=3, exp=1, mod=5
);
// Expected: 0x03
// Test 3: 5^0 mod 7 = 1 (zero exponent)
const input3 = Hex(
'0x' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'05' + '00' + '07' // base=5, exp=0, mod=7
);
// Expected: 0x01
// Test 4: Zero modulus error
const input4 = Hex(
'0x' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'0000000000000000000000000000000000000000000000000000000000000001' +
'02' + '03' + '00' // base=2, exp=3, mod=0
);
// Expected: Error (division by zero)
References
Specifications