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.
PackedUserOperation
PackedUserOperation represents an ERC-4337 v0.7+ user operation with optimized field packing. Gas limits and fees are packed into bytes32 fields, reducing calldata size and lowering costs for bundlers.
Quick Start
import { PackedUserOperation } from '@tevm/voltaire/primitives/PackedUserOperation';
import { UserOperation } from '@tevm/voltaire/primitives/UserOperation';
import { ENTRYPOINT_V07 } from '@tevm/voltaire/primitives/EntryPoint';
// Create from v0.6 UserOperation
const userOp = UserOperation.from({
sender: "0x742d35Cc6634C0532925a3b844Bc9e7595f251e3",
nonce: 0n,
initCode: "0x",
callData: "0x...",
callGasLimit: 100000n,
verificationGasLimit: 200000n,
preVerificationGas: 50000n,
maxFeePerGas: 1000000000n,
maxPriorityFeePerGas: 1000000000n,
paymasterAndData: "0x",
signature: "0x",
});
// Pack to v0.7
const packed = UserOperation.pack(userOp);
// Hash for signing
const hash = PackedUserOperation.hash(packed, ENTRYPOINT_V07, 1);
// Unpack back to v0.6
const unpacked = PackedUserOperation.unpack(packed);
Type Definition
export type PackedUserOperationType = {
readonly sender: AddressType;
readonly nonce: Uint256Type;
readonly initCode: Uint8Array;
readonly callData: Uint8Array;
readonly accountGasLimits: Uint8Array; // bytes32
readonly preVerificationGas: Uint256Type;
readonly gasFees: Uint8Array; // bytes32
readonly paymasterAndData: Uint8Array;
readonly signature: Uint8Array;
} & { readonly [brand]: "PackedUserOperation" };
Field Packing
accountGasLimits (bytes32)
Packs verification and call gas limits:
verificationGasLimit (128 bits) || callGasLimit (128 bits)
gasFees (bytes32)
Packs priority fee and max fee:
maxPriorityFeePerGas (128 bits) || maxFeePerGas (128 bits)
API Reference
from
Create PackedUserOperation from parameters.
function from(params: {
sender: AddressType | string | number | bigint | Uint8Array;
nonce: Uint256Type | bigint | number | string;
initCode: Uint8Array | string;
callData: Uint8Array | string;
accountGasLimits: Uint8Array | string; // 32 bytes
preVerificationGas: Uint256Type | bigint | number | string;
gasFees: Uint8Array | string; // 32 bytes
paymasterAndData: Uint8Array | string;
signature: Uint8Array | string;
}): PackedUserOperationType
hash
Compute userOpHash for signing.
function hash(
packedUserOp: PackedUserOperationType,
entryPoint: AddressType | string | number | bigint | Uint8Array,
chainId: bigint | number
): Uint8Array
unpack
Convert PackedUserOperation (v0.7) to UserOperation (v0.6).
function unpack(
packedUserOp: PackedUserOperationType
): UserOperationType
Packing Implementation
Pack Gas Limits
// accountGasLimits = verificationGasLimit (128) || callGasLimit (128)
const accountGasLimits = new Uint8Array(32);
// High 16 bytes: verificationGasLimit
let verification = verificationGasLimit;
for (let i = 15; i >= 0; i--) {
accountGasLimits[i] = Number(verification & 0xffn);
verification >>= 8n;
}
// Low 16 bytes: callGasLimit
let call = callGasLimit;
for (let i = 31; i >= 16; i--) {
accountGasLimits[i] = Number(call & 0xffn);
call >>= 8n;
}
Pack Gas Fees
// gasFees = maxPriorityFeePerGas (128) || maxFeePerGas (128)
const gasFees = new Uint8Array(32);
// High 16 bytes: maxPriorityFeePerGas
let priority = maxPriorityFeePerGas;
for (let i = 15; i >= 0; i--) {
gasFees[i] = Number(priority & 0xffn);
priority >>= 8n;
}
// Low 16 bytes: maxFeePerGas
let max = maxFeePerGas;
for (let i = 31; i >= 16; i--) {
gasFees[i] = Number(max & 0xffn);
max >>= 8n;
}
Benefits Over v0.6
- Reduced calldata: Smaller operation size
- Lower gas costs: Less data to submit
- Bundler efficiency: Lower costs for bundlers
- Backward compatible: Can convert to/from v0.6
Usage Patterns
Round-trip Conversion
import { UserOperation } from '@tevm/voltaire/primitives/UserOperation';
import { PackedUserOperation } from '@tevm/voltaire/primitives/PackedUserOperation';
// Start with v0.6
const userOp = UserOperation.from({
sender: "0x...",
nonce: 0n,
initCode: "0x",
callData: "0x...",
callGasLimit: 100000n,
verificationGasLimit: 200000n,
preVerificationGas: 50000n,
maxFeePerGas: 1500000000n,
maxPriorityFeePerGas: 1000000000n,
paymasterAndData: "0x",
signature: "0x",
});
// Pack to v0.7
const packed = UserOperation.pack(userOp);
// Unpack back to v0.6
const unpacked = PackedUserOperation.unpack(packed);
// Values match
console.log(unpacked.callGasLimit === userOp.callGasLimit); // true
console.log(unpacked.maxFeePerGas === userOp.maxFeePerGas); // true
Direct Creation
import { PackedUserOperation } from '@tevm/voltaire/primitives/PackedUserOperation';
// Manually create packed fields
const accountGasLimits = new Uint8Array(32);
// ... pack verification and call gas
const gasFees = new Uint8Array(32);
// ... pack priority and max fees
const packed = PackedUserOperation.from({
sender: "0x...",
nonce: 0n,
initCode: "0x",
callData: "0x...",
accountGasLimits,
preVerificationGas: 50000n,
gasFees,
paymasterAndData: "0x",
signature: "0x",
});
References