Skip to main content
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: 0x000000000000000000000000000000000000000b Introduced: Prague (EIP-2537) EIP: EIP-2537 The BLS12-381 G1 Add precompile performs elliptic curve point addition on the BLS12-381 curve’s G1 group. It takes two G1 points and returns their sum. This is essential for BLS signature verification, consensus protocols, and advanced cryptographic applications. EIP-2537 introduces BLS12-381 curve operations to Ethereum, enabling efficient BLS signatures used in Ethereum 2.0 consensus and other cryptographic protocols requiring pairing-friendly curves. The BLS12-381 curve is a pairing-friendly elliptic curve designed for zkSNARKs and signature aggregation, offering 128-bit security with efficient pairing operations.

Gas Cost

Fixed: 500 gas

Input Format

Offset | Length | Description
-------|--------|-------------
0      | 64     | x1 (first point x-coordinate, big-endian, left-padded)
64     | 64     | y1 (first point y-coordinate, big-endian, left-padded)
128    | 64     | x2 (second point x-coordinate, big-endian, left-padded)
192    | 64     | y2 (second point y-coordinate, big-endian, left-padded)
Total input length: 256 bytes (exactly) Points must satisfy the curve equation: y^2 = x^3 + 4 over the BLS12-381 base field (Fp). Point at infinity is represented as all zeros (128 bytes of zeros for each point).

Output Format

Offset | Length | Description
-------|--------|-------------
0      | 64     | x (result point x-coordinate, big-endian, left-padded)
64     | 64     | y (result point y-coordinate, big-endian, left-padded)
Total output length: 128 bytes

TypeScript Example

import { execute, PrecompileAddress } from '@tevm/voltaire/precompiles';
import { Hardfork } from '@tevm/voltaire/primitives/Hardfork';

// BLS12-381 G1 generator point (48 bytes, left-padded to 64)
const g1_x = Bytes64('0x000000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb');
const g1_y = Bytes64('0x0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1');

// Add generator point to itself: G + G = 2G
const input = new Uint8Array(256);
input.set(g1_x, 0);
input.set(g1_y, 64);
input.set(g1_x, 128);
input.set(g1_y, 192);

const result = execute(
  PrecompileAddress.BLS12_G1_ADD,
  input,
  1000n,
  Hardfork.PRAGUE
);

if (result.success) {
  const resultX = result.output.slice(0, 64);
  const resultY = result.output.slice(64, 128);
  console.log('Result: 2*G');
  console.log('Gas used:', result.gasUsed); // 500
} else {
  console.error('Error:', result.error);
}

Zig Example

const std = @import("std");
const precompiles = @import("precompiles");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // BLS12-381 G1 generator coordinates (48 bytes, padded to 64)
    const g1_x = [_]u8{
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x17, 0xf1, 0xd3, 0xa7, 0x31, 0x97, 0xd7, 0x94, 0x26, 0x95, 0x63, 0x8c, 0x4f, 0xa9, 0xac, 0x0f,
        0xc3, 0x68, 0x8c, 0x4f, 0x97, 0x74, 0xb9, 0x05, 0xa1, 0x4e, 0x3a, 0x3f, 0x17, 0x1b, 0xac, 0x58,
        0x6c, 0x55, 0xe8, 0x3f, 0xf9, 0x7a, 0x1a, 0xef, 0xfb, 0x3a, 0xf0, 0x0a, 0xdb, 0x22, 0xc6, 0xbb,
    };
    const g1_y = [_]u8{
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x08, 0xb3, 0xf4, 0x81, 0xe3, 0xaa, 0xa0, 0xf1, 0xa0, 0x9e, 0x30, 0xed, 0x74, 0x1d, 0x8a, 0xe4,
        0xfc, 0xf5, 0xe0, 0x95, 0xd5, 0xd0, 0x0a, 0xf6, 0x00, 0xdb, 0x18, 0xcb, 0x2c, 0x04, 0xb3, 0xed,
        0xd0, 0x3c, 0xc7, 0x44, 0xa2, 0x88, 0x8a, 0xe4, 0x0c, 0xaa, 0x23, 0x29, 0x46, 0xc5, 0xe7, 0xe1,
    };

    // Construct input: G + G
    var input: [256]u8 = [_]u8{0} ** 256;
    @memcpy(input[0..64], &g1_x);
    @memcpy(input[64..128], &g1_y);
    @memcpy(input[128..192], &g1_x);
    @memcpy(input[192..256], &g1_y);

    const result = try precompiles.bls12_g1_add.execute(allocator, &input, 1000);
    defer result.deinit(allocator);

    std.debug.print("Result: 2*G\n", .{});
    std.debug.print("Gas used: {}\n", .{result.gas_used});
}

Error Conditions

  • Out of gas: gasLimit < 500
  • Invalid input length: input.len != 256
  • Invalid point: Point coordinates don’t satisfy curve equation y² = x³ + 4
  • Point not in subgroup: Point not in correct subgroup (fails validation)
  • Coordinate out of range: x or y >= field modulus
Invalid inputs cause precompile to return error.InvalidPoint.

Use Cases

  • BLS signature verification: Aggregating and verifying BLS signatures
  • Ethereum 2.0 consensus: Validator signature aggregation
  • zkSNARKs on BLS12-381: Proof systems using BLS12-381 curve
  • Distributed key generation: Threshold cryptography protocols
  • Verifiable delay functions: VDFs using pairings
  • Privacy protocols: zk-Rollups and privacy-preserving systems

Implementation Details

  • Zig: Uses blst library (C) via crypto module for G1 operations
  • TypeScript: Uses @noble/curves bls12-381 implementation
  • Curve: BLS12-381 with embedding degree 12
  • Field modulus (p): 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
  • Group order (r): 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001

BLS12-381 G1 Parameters

  • Curve equation: y² = x³ + 4
  • Base field: Fp (381-bit prime)
  • Coordinate size: 48 bytes (padded to 64 bytes in precompile encoding)
  • Generator G1 x: 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb
  • Generator G1 y: 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1
  • Point at infinity: All zeros (128 bytes)

Point Addition Rules

  • P + O = P (identity element)
  • O + P = P (identity is commutative)
  • P + P = 2P (point doubling)
  • P + (-P) = O (inverse gives infinity)
  • General addition uses elliptic curve group law

Security Considerations

  • All coordinates must be validated to be in field and on curve
  • Points must be checked for subgroup membership
  • Implementation uses constant-time operations where possible
  • Uses battle-tested blst library for security-critical operations

Performance Notes

  • Fixed gas cost makes G1 addition predictable
  • More efficient than generic elliptic curve operations
  • Hardware acceleration available on some platforms via blst
  • Point validation adds overhead but is necessary for security