Transactions
Building, signing, and sending Ethereum transactions with eth.zig.
eth.zig supports all Ethereum transaction types -- Legacy, EIP-2930 (access lists), EIP-1559 (priority fees), EIP-4844 (blob transactions), and EIP-7702 (set-code authorizations).
Sending a Transaction
The Wallet handles the full lifecycle: auto-filling nonce, gas, and chain ID from the provider, then signing and broadcasting.
const eth = @import("eth");
var transport = eth.http_transport.HttpTransport.init(allocator, "https://rpc.example.com", eth.runtime.blockingIo());
defer transport.deinit();
var provider = eth.provider.Provider.init(allocator, &transport);
const private_key = try eth.hex.hexToBytesFixed(32, "your_private_key_hex");
var wallet = eth.wallet.Wallet.init(allocator, private_key, &provider);
// Send 1 ETH -- nonce, gas, and chain_id are auto-filled
const tx_hash = try wallet.sendTransaction(.{
.to = recipient_address,
.value = eth.units.parseEther(1.0),
});SendTransactionOpts
All fields in SendTransactionOpts are optional with sensible defaults. Fields like nonce, gas_limit, and fee parameters are auto-filled from the provider when not specified:
| Field | Type | Default |
|---|---|---|
to | ?[20]u8 | null |
value | u256 | 0 |
data | []const u8 | &.{} |
gas_limit | ?u64 | Auto (estimateGas) |
max_fee_per_gas | ?u256 | Auto (getGasPrice) |
max_priority_fee_per_gas | ?u256 | Auto (getMaxPriorityFeePerGas) |
nonce | ?u64 | Auto (getTransactionCount) |
Transaction Types
Legacy (Type 0)
Pre-EIP-1559 transactions with a single gas_price:
const tx = eth.transaction.Transaction{ .legacy = .{
.nonce = 0,
.gas_price = 20_000_000_000, // 20 Gwei
.gas_limit = 21_000,
.to = recipient,
.value = eth.units.parseEther(1.0),
.data = &.{},
.chain_id = 1,
}};EIP-1559 (Type 2)
Modern transactions with base fee + priority fee:
const tx = eth.transaction.Transaction{ .eip1559 = .{
.chain_id = 1,
.nonce = 0,
.max_priority_fee_per_gas = 1_000_000_000, // 1 Gwei tip
.max_fee_per_gas = 30_000_000_000, // 30 Gwei max
.gas_limit = 21_000,
.to = recipient,
.value = eth.units.parseEther(1.0),
.data = &.{},
.access_list = &.{},
}};EIP-4844 (Type 3)
Blob transactions for L2 data availability:
const tx = eth.transaction.Transaction{ .eip4844 = .{
.chain_id = 1,
.nonce = 0,
.max_priority_fee_per_gas = 1_000_000_000,
.max_fee_per_gas = 30_000_000_000,
.gas_limit = 21_000,
.to = recipient,
.value = 0,
.data = &.{},
.access_list = &.{},
.max_fee_per_blob_gas = 1_000_000_000,
.blob_versioned_hashes = &.{blob_hash},
}};EIP-7702 (Type 4)
Set-code transactions (Pectra) let an EOA delegate its code to a contract via
one or more signed authorization tuples. Each authorization is signed over
keccak256(0x05 || rlp([chain_id, address, nonce])), and the recovered signer
is the authority whose code is being set. Note that EIP-7702's to field is
not nullable -- type-0x04 transactions cannot create contracts.
Sign an authorization with signer.signAuthorization, then attach it to the
transaction. A chain_id of 0 makes the authorization valid on any chain.
const eth = @import("eth");
// The authority (the EOA whose code is being delegated) signs the authorization.
var authority = eth.signer.Signer.init(authority_private_key);
defer authority.deinit();
// Delegate `authority`'s code to `delegate_contract` for chain 1, nonce 0.
const auth = try authority.signAuthorization(
allocator,
1, // chain_id (0 = valid on any chain)
delegate_contract, // [20]u8 delegation target
0, // authority account nonce
);
const auths = [_]eth.transaction.Authorization{auth};
const tx = eth.transaction.Transaction{ .eip7702 = .{
.chain_id = 1,
.nonce = 0,
.max_priority_fee_per_gas = 1_000_000_000, // 1 Gwei tip
.max_fee_per_gas = 30_000_000_000, // 30 Gwei max
.gas_limit = 100_000,
.to = recipient, // required -- no contract creation
.value = 0,
.data = &.{},
.access_list = &.{},
.authorization_list = &auths,
}};
// Sign the transaction with the sender's key and broadcast.
var sender = eth.signer.Signer.init(sender_private_key);
defer sender.deinit();
const sighash = try eth.transaction.hashForSigning(allocator, tx);
const sig = try sender.signHash(sighash);
const raw = try eth.transaction.serializeSigned(allocator, tx, sig.r, sig.s, sig.v);
defer allocator.free(raw);
const tx_hash = try provider.sendRawTransaction(raw);Serialization
Serialize transactions for signing or broadcasting:
// Serialize for signing (produces the sighash payload)
const serialized = try eth.transaction.serializeForSigning(allocator, tx);
defer allocator.free(serialized);
// Hash the serialized transaction
const sighash = eth.keccak.keccak256(serialized);Unit Conversions
const eth_mod = @import("eth");
const one_ether = eth_mod.units.parseEther(1.0); // 1000000000000000000
const one_gwei = eth_mod.units.parseGwei(1.0); // 1000000000Examples
Runnable code examples for eth.zig -- address derivation, signing, ERC-20, HD wallets, and more.
KZG / EIP-4844
Build EIP-4844 blob-transaction sidecars - KZG commitments and proofs over raw blob data - using the vendored c-kzg-4844 and blst reference implementations with the mainnet trusted setup embedded.