Meta-transactions
Overview
The Meta-Transaction system provides a standardized way to create and sign meta-transactions for the Bloxchain system. This utility leverages the contract's own EIP-712 message hash generation to ensure signature compatibility and avoid JavaScript replication issues.
Key Features
Contract-Based Message Hash Generation: Uses the contract's
generateUnsignedMetaTransactionForNew/ForExistingfunctionsType-Safe Implementation: Full TypeScript support with proper type definitions
Automatic Signature Verification: Built-in signature verification and validation
Support for Multiple Actions: Request, approve, and cancel operations
Comprehensive Error Handling: Detailed error messages and validation
Flexible Workflows: Support for programmatic, frontend wallet, and hybrid integration patterns
Architecture
Three-Step Process
Create Unsigned Meta-Transaction: Contract generates EIP-712 message hash
Sign Message Hash: Either programmatically or via external wallet
Verify & Complete: Verify signature and return complete meta-transaction
Core Components
MetaTransactionSigner: Main class for creating and signing meta-transactions
MetaTransactionBuilder: Helper class for creating parameter structures
Type Definitions: Comprehensive TypeScript interfaces for all structures
Security Model
The signing process follows these security principles:
Contract-First Approach: Message hash generation is delegated to the contract
EIP-712 Compliance: Full compliance with EIP-712 standard
Signature Verification: Automatic verification of generated signatures
Permission Validation: Contract-level permission checking
Entrypoint binding: On-chain verification requires
MetaTxParams.handlerSelectorto equal the selector of the actual public function used to submit the meta-transaction, andhandlerContractto equal the verifying account address (same as the EIP-712verifyingContract). Do not sign one handler and submit through a different sibling wrapper.Raw digest signing: The contract’s
messagefield is the final EIP-712 digest (\x19\x01‖ domain ‖ structHash). Wallets must not usepersonal_signon that 32-byte value—personal_signwraps the payload with the EIP-191 “Ethereum signed message” prefix, soecrecoveron-chain will fail. Use ViemsignMessage({ message: { raw: digest } }),signTypedDatawith the same domain/types asEngineBlox(seesignMetaTransactionWithWalletinmetaTransaction.tsx), or a localsign({ hash })for backend keys.Per-signer sequential nonce (replay control, not a throughput bug): On-chain,
MetaTxParams.noncemust equal the currentgetSignerNonce(signer)for that signer; after a successful meta path the contract increments only that signer’s nonce. Different signers have independent nonce counters, so meta-transactions from distinct signers do not serialize each other. For a single signer, relayers must submit in order—parallel unrelated intents from the same address without coordination would break replay semantics. NewSIGN_META_REQUEST_AND_APPROVEflows also align with sequentialtxCounter/txIdallocation.Predictable
txIdinSIGN_META_REQUEST_AND_APPROVE: The nexttxIdis derived fromtxCounter, which is observable on-chain. A griefing party could front-run to bump the counter and invalidate a pre-signedtxId. Mitigations: observetxCounterimmediately before signing, use short deadlines, and monitor the mempool for counter-bumping transactions. This is the same operational model as sequential nonces, not a missing check.No owner-driven signature invalidation: The protocol does not provide a global "revoke all pending signatures" mechanism. Signed-but-unsubmitted meta-transactions remain valid until their deadline expires or the signer's nonce is consumed (by submitting any valid meta-tx for that signer). To invalidate a leaked signature: (a) submit a cheap no-op meta-tx to advance the signer's nonce past the leaked one, (b) rotate the signer off the role via RBAC batch, or (c) wait for deadline. A blanket "owner revoke bitmap" would be a protocol-level addition.
Workflow Patterns
Pattern 1: Programmatic Signing (Backend/Node.js)
Pattern 2: Frontend Wallet Integration
Use a Viem wallet client and sign the raw contract digest. This matches on-chain ecrecover and mirrors Guard Controller examples (signMessage with message: { raw: metaTx.message }).
Pattern 3: Hybrid Approach (Frontend + Backend)
Usage Examples
Basic Setup
Creating a New Meta-Transaction
Approving an Existing Transaction
Canceling a Transaction
Frontend Wallet Integration Example
See also Guard Controller for the same signMessage({ message: { raw } }) pattern on a full config flow.
Backend Service Example
Transaction Actions
The system supports the following transaction actions:
SIGN_META_REQUEST_AND_APPROVE
Request and immediately approve a new transaction
Single-step operations
SIGN_META_APPROVE
Approve an existing pending transaction
Multi-step approval process
SIGN_META_CANCEL
Cancel an existing pending transaction
Emergency cancellation
Execution Types
Standard Execution
For standard function calls with encoded parameters:
Raw Execution
For custom transaction data:
API Reference
Core Methods
createUnsignedMetaTransactionForNew(txParams, metaTxParams)
Creates an unsigned meta-transaction for a new operation.
No wallet client required
Returns:
MetaTransactionwith contract-generated message hashUse case: Frontend wallet integration, hybrid workflows
createUnsignedMetaTransactionForExisting(txId, metaTxParams)
Creates an unsigned meta-transaction for an existing operation.
No wallet client required
Returns:
MetaTransactionwith contract-generated message hashUse case: Approving/canceling existing transactions
signMetaTransactionWithWallet(unsignedMetaTx)
Signs using the configured walletClient and signTypedData with the SDK’s META_TX_DOMAIN / META_TX_TYPES (must match EngineBlox on-chain).
Requires
walletClientwith an active accountReturns: Complete signed
MetaTransactionUse case: Browser or server wallet without exporting the private key
signMetaTransaction(unsignedMetaTx, signerAddress, privateKey)
Signs the contract-returned digest with account.sign({ hash }) (raw digest; no EIP-191 prefix).
Requires the signer’s private key
Returns: Complete signed
MetaTransactionUse case: Backend automation, tests, custodial signers
createSignedMetaTransactionWithSignature(unsignedMetaTx, signature)
Creates a signed meta-transaction with an external signature.
No wallet client required
Returns: Complete signed
MetaTransactionUse case: Frontend wallet integration
Convenience Methods
createSignedMetaTransactionForNew(txParams, metaTxParams, signerAddress, privateKey)
Combines unsigned creation and private-key signing for new operations.
createSignedMetaTransactionForExisting(txId, metaTxParams, signerAddress, privateKey)
Combines unsigned creation and private-key signing for existing operations.
MetaTransactionSigner Class
Constructor
Methods
signMetaTransactionWithWallet(unsignedMetaTx): Promise<MetaTransaction>signMetaTransaction(unsignedMetaTx, signerAddress, privateKey): Promise<MetaTransaction>createSignedMetaTransactionForNew(txParams, metaTxParams, signerAddress, privateKey): Promise<MetaTransaction>createSignedMetaTransactionForExisting(txId, metaTxParams, signerAddress, privateKey): Promise<MetaTransaction>
MetaTransactionBuilder Class
Static Methods
createStandardExecutionOptions(functionSelector, params): HexcreateRawExecutionOptions(rawTxData): HexcreateMetaTxParams(handlerContract, handlerSelector, action, deadline, maxGasPrice, signer, chainId?, nonce?): MetaTxParamscreateTxParams(requester, target, value, gasLimit, operationType, executionType, executionOptions): TxParams
Contract Integration Points
EngineBlox.sol Functions Used
generateUnsignedForNewMetaTx()- Creates unsigned meta-transaction for new operationsgenerateUnsignedForExistingMetaTx()- Creates unsigned meta-transaction for existing operationsgenerateMessageHash()- Generates EIP-712 message hash (called internally by above functions)createMetaTxParams()- Helper function for creating meta-transaction parameters
EIP-712 Implementation
The contract implements EIP-712 with:
Domain:
Bloxchain, version1.0.0Chain ID: Current blockchain chain ID
Verifying Contract: The contract address
Type Hash: Complex nested structure for MetaTransaction
Signature Verification Flow
Contract generates the EIP-712 digest (
message) using its own implementation.The client signs that digest without an extra EIP-191 wrapper: e.g. Viem
signMessage({ message: { raw: message } }), SDKsignMetaTransactionWithWallet(typed data matching the contract), orsign({ hash })for local keys.The SDK verifies locally in
createSignedMetaTransactionWithSignature/signMetaTransactionpaths before returning.The contract recovers the signer from the same digest during execution.
Security Considerations
Message Hash Generation
The utility uses the contract's own EIP-712 message hash generation to ensure:
Exact Compliance: Perfect alignment with on-chain implementation
No Replication Issues: Avoids JavaScript implementation differences
Future Compatibility: Automatically adapts to contract updates
Signature Verification
All signatures are automatically verified:
Message Hash Validation: Ensures the message hash is valid
Signer Recovery: Recovers the signer address from the signature
Address Matching: Verifies the recovered address matches the expected signer
Permission Validation
The contract performs additional validation:
Role-Based Access: Checks if the signer has appropriate roles
Function Permissions: Validates function-specific permissions
Action Authorization: Ensures the action is allowed for the function
Error Handling
The utility provides comprehensive error handling:
Best Practices
Parameter Validation
Always validate parameters before creating meta-transactions:
Error Handling
Implement comprehensive error handling:
Gas Optimization
Optimize gas usage:
Troubleshooting
Invalid signature / wrong signer (EIP-191 vs raw digest)
If the SDK throws Signature verification failed or the contract reverts on meta-tx submit:
Wrong:
personal_sign(or any API that applies the EIP-191 “Ethereum signed message” prefix) onunsignedMetaTx.message. The contract hashes\x19\x01‖ domain ‖ structHash once; adding the text-message prefix produces a different digest.Right: Viem
walletClient.signMessage({ message: { raw: unsignedMetaTx.message }, account }), or SDKsignMetaTransactionWithWallet(unsignedMetaTx)with a wallet client, orsignMetaTransaction/sign({ hash })with the raw digest for local keys.
Full flow examples: Guard Controller (raw digest signing).
Common Issues
Wallet client missing: Required for
signMetaTransactionWithWallet; usesignMessage+createSignedMetaTransactionWithSignatureif you only have an EIP-1193 provider and buildcreateWalletClientyourself.Contract address invalid: Verify the contract address is correct
Signature verification failed: Confirm the signing API above; confirm
params.signermatches the account that signedPermission denied: Verify the signer has appropriate roles
Deadline expired: Ensure the deadline is in the future
Debug Information
Enable debug logging:
Integration with Bloxchain System
Contract Integration
The utility integrates with the Bloxchain system through:
SecureOwnable Contract: Uses the contract's meta-transaction functions
EngineBlox Library: Leverages the library's EIP-712 implementation
Dynamic RBAC: Integrates with the role-based access control system
Workflow Integration
Meta-transactions integrate into the Bloxchain workflow:
Request Phase: Create and sign meta-transaction for new operations
Approval Phase: Sign meta-transactions to approve pending operations
Execution Phase: Contract executes the operation after verification
Completion Phase: Transaction status is updated based on execution result
Benefits of Separated Workflow
Flexibility
Frontend Integration: Support for MetaMask, WalletConnect, etc.
Backend Processing: Programmatic signing for automated flows
Hybrid Approaches: Combine frontend UX with backend security
Security
Contract-First: Message hash generation always uses contract
Signature Verification: Automatic verification before completion
Type Safety: Full TypeScript support throughout
Developer Experience
Simple API: Clear separation of concerns
Multiple Patterns: Support for different integration needs
Error Handling: Clean error propagation and handling
Migration Guide
From Previous Version
File Structure
The meta-transaction utilities are organized in a dedicated folder:
Benefits of This Structure
Clean Separation: Meta-transaction utilities are isolated
External ABI: ABI is separate from code logic
Focused Scope: Only meta-transaction related functionality
Maintainability: Easy to update ABI without touching code
Reusable ABI: ABI can be used by other tools/utilities
Production Readiness
✅ Code Quality
✅ Security
✅ Compatibility
✅ Usability
Conclusion
The Meta-Transaction system provides a robust, secure, and easy-to-use solution for creating and signing meta-transactions in the Bloxchain system. By leveraging the contract's own EIP-712 implementation, it ensures perfect compatibility and eliminates common JavaScript replication issues.
The new architecture provides the same functionality while enabling new integration patterns for frontend wallets and hybrid workflows, making it suitable for both backend automation and frontend user interactions.
Last updated
Was this helpful?