For the complete documentation index, see llms.txt. This page is also available as Markdown.

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/ForExisting functions

  • Type-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

  1. Create Unsigned Meta-Transaction: Contract generates EIP-712 message hash

  2. Sign Message Hash: Either programmatically or via external wallet

  3. Verify & Complete: Verify signature and return complete meta-transaction

Core Components

  1. MetaTransactionSigner: Main class for creating and signing meta-transactions

  2. MetaTransactionBuilder: Helper class for creating parameter structures

  3. Type Definitions: Comprehensive TypeScript interfaces for all structures

Security Model

The signing process follows these security principles:

  1. Contract-First Approach: Message hash generation is delegated to the contract

  2. EIP-712 Compliance: Full compliance with EIP-712 standard

  3. Signature Verification: Automatic verification of generated signatures

  4. Permission Validation: Contract-level permission checking

  5. Entrypoint binding: On-chain verification requires MetaTxParams.handlerSelector to equal the selector of the actual public function used to submit the meta-transaction, and handlerContract to equal the verifying account address (same as the EIP-712 verifyingContract). Do not sign one handler and submit through a different sibling wrapper.

  6. Raw digest signing: The contract’s message field is the final EIP-712 digest (\x19\x01 ‖ domain ‖ structHash). Wallets must not use personal_sign on that 32-byte value—personal_sign wraps the payload with the EIP-191 “Ethereum signed message” prefix, so ecrecover on-chain will fail. Use Viem signMessage({ message: { raw: digest } }), signTypedData with the same domain/types as EngineBlox (see signMetaTransactionWithWallet in metaTransaction.tsx), or a local sign({ hash }) for backend keys.

  7. Per-signer sequential nonce (replay control, not a throughput bug): On-chain, MetaTxParams.nonce must equal the current getSignerNonce(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. New SIGN_META_REQUEST_AND_APPROVE flows also align with sequential txCounter / txId allocation.

  8. Predictable txId in SIGN_META_REQUEST_AND_APPROVE: The next txId is derived from txCounter, which is observable on-chain. A griefing party could front-run to bump the counter and invalidate a pre-signed txId. Mitigations: observe txCounter immediately 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.

  9. 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:

Action
Description
Use Case

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: MetaTransaction with contract-generated message hash

  • Use case: Frontend wallet integration, hybrid workflows

createUnsignedMetaTransactionForExisting(txId, metaTxParams)

Creates an unsigned meta-transaction for an existing operation.

  • No wallet client required

  • Returns: MetaTransaction with contract-generated message hash

  • Use 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 walletClient with an active account

  • Returns: Complete signed MetaTransaction

  • Use 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 MetaTransaction

  • Use 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 MetaTransaction

  • Use 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): Hex

  • createRawExecutionOptions(rawTxData): Hex

  • createMetaTxParams(handlerContract, handlerSelector, action, deadline, maxGasPrice, signer, chainId?, nonce?): MetaTxParams

  • createTxParams(requester, target, value, gasLimit, operationType, executionType, executionOptions): TxParams

Contract Integration Points

EngineBlox.sol Functions Used

  1. generateUnsignedForNewMetaTx() - Creates unsigned meta-transaction for new operations

  2. generateUnsignedForExistingMetaTx() - Creates unsigned meta-transaction for existing operations

  3. generateMessageHash() - Generates EIP-712 message hash (called internally by above functions)

  4. createMetaTxParams() - Helper function for creating meta-transaction parameters

EIP-712 Implementation

The contract implements EIP-712 with:

  • Domain: Bloxchain, version 1.0.0

  • Chain ID: Current blockchain chain ID

  • Verifying Contract: The contract address

  • Type Hash: Complex nested structure for MetaTransaction

Signature Verification Flow

  1. Contract generates the EIP-712 digest (message) using its own implementation.

  2. The client signs that digest without an extra EIP-191 wrapper: e.g. Viem signMessage({ message: { raw: message } }), SDK signMetaTransactionWithWallet (typed data matching the contract), or sign({ hash }) for local keys.

  3. The SDK verifies locally in createSignedMetaTransactionWithSignature / signMetaTransaction paths before returning.

  4. 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:

  1. Exact Compliance: Perfect alignment with on-chain implementation

  2. No Replication Issues: Avoids JavaScript implementation differences

  3. Future Compatibility: Automatically adapts to contract updates

Signature Verification

All signatures are automatically verified:

  1. Message Hash Validation: Ensures the message hash is valid

  2. Signer Recovery: Recovers the signer address from the signature

  3. Address Matching: Verifies the recovered address matches the expected signer

Permission Validation

The contract performs additional validation:

  1. Role-Based Access: Checks if the signer has appropriate roles

  2. Function Permissions: Validates function-specific permissions

  3. 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) on unsignedMetaTx.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 SDK signMetaTransactionWithWallet(unsignedMetaTx) with a wallet client, or signMetaTransaction / sign({ hash }) with the raw digest for local keys.

Full flow examples: Guard Controller (raw digest signing).

Common Issues

  1. Wallet client missing: Required for signMetaTransactionWithWallet; use signMessage + createSignedMetaTransactionWithSignature if you only have an EIP-1193 provider and build createWalletClient yourself.

  2. Contract address invalid: Verify the contract address is correct

  3. Signature verification failed: Confirm the signing API above; confirm params.signer matches the account that signed

  4. Permission denied: Verify the signer has appropriate roles

  5. 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:

  1. SecureOwnable Contract: Uses the contract's meta-transaction functions

  2. EngineBlox Library: Leverages the library's EIP-712 implementation

  3. Dynamic RBAC: Integrates with the role-based access control system

Workflow Integration

Meta-transactions integrate into the Bloxchain workflow:

  1. Request Phase: Create and sign meta-transaction for new operations

  2. Approval Phase: Sign meta-transactions to approve pending operations

  3. Execution Phase: Contract executes the operation after verification

  4. 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

  1. Clean Separation: Meta-transaction utilities are isolated

  2. External ABI: ABI is separate from code logic

  3. Focused Scope: Only meta-transaction related functionality

  4. Maintainability: Easy to update ABI without touching code

  5. 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?