UserOp Validator is a robust, standalone validation library for ERC-4337 UserOperations. It is designed to act as a reference implementation for the validation logic performed by Bundlers, ensuring that UserOperations are safe to include in a bundle.
It fully implements the strict validation rules defined in EIP-7562, protecting the network from DoS attacks.
PackedUserOperation before execution.validateUserOp execution using a real EVM (@ethereumjs/vm) environment.GASPRICE, TIMESTAMP) during validation.preVerificationGas.verificationGasLimit and fee parameters against network standards.Install the package via npm:
npm install userop-validator
The easiest way to check a UserOperation is using the CLI. You can provide a JSON file containing the PackedUserOperation object.
Example JSON (userop.json):
{
"sender": "0x...",
"nonce": "0x...",
"initCode": "0x...",
"callData": "0x...",
"accountGasLimits": "0x...",
"preVerificationGas": "0x...",
"gasFees": "0x...",
"paymasterAndData": "0x...",
"signature": "0x..."
}
Run Validation:
# Using npx
npx userop-validator ./userop.json
# Output:
# Validating UserOp from: ./userop.json
# Static Validation Passed ✅
You can integrate the validator into your own TypeScript/JavaScript projects (e.g., a custom Bundler, a Wallet, or a testing tool).
Perform cheap, fast checks on the structure and limits.
import { validateUserOpStructure } from 'userop-validator/dist/static-checks';
const userOp = { ... }; // Your PackedUserOperation object
const result = validateUserOpStructure(userOp);
if (!result.isValid) {
console.error("Validation Errors:", result.errors);
} else {
console.log("Structure is valid!");
}
To perform the deep validation (simulating the EVM execution), you need to set up a VM instance and use the validator context.
import { VM } from '@ethereumjs/vm';
import { Address } from '@ethereumjs/util';
import {
validateExecutionRules,
createValidationContext,
EntityType
} from 'userop-validator/dist/validator';
// 1. Initialize VM (forked or new)
const vm = await VM.create();
// 2. Prepare Context
// Defines who is executing and what rules to apply
const context = createValidationContext({
sender: Address.fromString("0xSenderAddress..."),
entryPoint: Address.fromString("0xEntryPointAddress..."),
paymaster: Address.fromString("0xPaymasterAddress..."), // optional
factory: Address.fromString("0xFactoryAddress..."), // optional
});
// 3. Attach Validator Hook
// This injects the EIP-7562 rules into the EVM step loop
const cleanup = validateExecutionRules(vm, context);
try {
// 4. Run the simulation
// e.g., vm.runCall({ ... }) calling EntryPoint.validateUserOp
await vm.runCall({
to: context.entryPoint,
data: Buffer.from("..."), // encoded validateUserOp call
// ...
});
// Check for violations collected during execution
if (context.violations.length > 0) {
console.error("Validation Violations:", context.violations);
}
} finally {
// 5. Cleanup hooks
cleanup();
}
The validator tracks every SLOAD and SSTORE operation.
slot == address).preVerificationGas is sufficient to cover this cost.Contributions are welcome!
npm install.npm test.MIT