Skip to main content
Once you’ve received the payloads from the API, you need to sign them to approve execution.

Signature Formats by Mode and Version

MEE v2.2.1 introduces EIP-712 typed data signing for smart-account mode, providing better security and user experience through structured, human-readable signing requests.
ModeAccount VersionSignature FormatSigning Method
smart-accountv2.2.1EIP-712 Typed Dataeth_signTypedData_v4
smart-accountv2.1.0 (upgrade flow)Personal Messageeth_sign / personal_sign
eoa (Fusion)AnyPermit / OnchainMode-specific
eoa-7702v2.1.0Personal Messageeth_sign / personal_sign
The API automatically returns the correct payloadToSign format based on your account version and mode. Your signing code should detect and handle both formats.

Quick Start: Copy-Paste Utilities

For TypeScript users: Copy the utilities below directly into your project.
Not using TypeScript? Implement your own version following the same logic shown in the code.
Using a single execution mode? The utility below handles all signature types, but each mode only uses a subset:
ModeSignature Types Used
smart-accountsimple only (EIP-712 or personal message)
eoa-7702simple only (personal message)
eoa (Fusion)permit or onchain only
If your application uses only one mode, you can create a lighter implementation that handles just the signature types you’ll encounter.

How It Works: Three Signature Types

  • The API returns an array of payloads to sign.
  • In most cases this array has just one item.
  • Each item in the payload is one of three signature types (simple, permit, onchain) depending on your execution mode and token support

Simple (Smart Account & EIP-7702)

What: Signs a message for smart account or EIP-7702 execution
When: smart-account mode or eoa-7702 mode
Result: Off-chain signature
By default, v2.2.1 uses EIP-712 typed data for smart-account mode. Personal message signing only occurs in two cases: eoa-7702 mode, or when upgrading legacy v2.1.0 smart accounts.

EIP-712 Typed Data (Default for Smart Accounts)

For v2.2.1 smart accounts (the default), the payload contains EIP-712 structured data:
// Example payload from API (v2.2.1 smart account)
{
  domain: {
    name: "Nexus"
  },
  types: {
    MeeUserOp: [
      { name: "userOpHash", type: "bytes32" },
      { name: "lowerBoundTimestamp", type: "uint256" },
      { name: "upperBoundTimestamp", type: "uint256" }
    ],
    SuperTx: [
      { name: "meeUserOps", type: "MeeUserOp[]" }
    ]
  },
  primaryType: "SuperTx",
  message: {
    meeUserOps: [
      {
        userOpHash: "0xa6c23856046666c02f7a85f0b2e7dbc78beac25db5c1955ddc9eb78e0237fc9f",
        lowerBoundTimestamp: 0,
        upperBoundTimestamp: 1765465749
      }
    ]
  }
}
How to sign EIP-712:
const signature = await walletClient.signTypedData({
  ...payload,
  account
});

Personal Message (EIP-7702 & Legacy Upgrade Only)

For eoa-7702 mode and v2.1.0 accounts during upgrade, the payload contains a raw message:
// Example payload from API (v2.1.0 or eoa-7702)
{
  message: {
    raw: "0xed96a92aa94b8b1927fc7c52ca3b3fcd0d706147dfbeda34a62dc232f6993029"
  }
}
How to sign personal message:
const signature = await walletClient.signMessage({
  ...payload,
  account
});

Detecting the Format

Use a type guard to automatically detect and sign the correct format:
// Type guard to detect EIP-712 typed data
function isTypedDataSignature(payload: unknown): payload is TypedDataPayload {
  return typeof payload === 'object' && payload !== null
    && 'domain' in payload && 'primaryType' in payload;
}

// Sign based on detected format
const signature = isTypedDataSignature(payload)
  ? await walletClient.signTypedData({ ...payload, account })  // EIP-712 for v2.2.1
  : await walletClient.signMessage({ ...payload, account });   // Personal message for v2.1.0/7702

The Magic: Supertx Hash Embedding

For EOA mode, the API cleverly embeds the supertransaction hash into your signatures. This creates a cryptographic link between funding and execution, enabling single-signature cross-chain operations. For Permit: The supertx hash replaces the deadline field
// Before: deadline = 1700000000
// After:  deadline = "0xabcdef1234567890..." (supertx hash)
For Onchain: The supertx hash is appended to calldata
// Before: 0xa9059cbb000000000000000000000000...
// After:  0xa9059cbb000000000000000000000000...abcdef1234567890

Usage by Execution Mode

Signature Requirements:
  • One signature per funding token
  • Type depends on token support (permit or onchain)
// 1. Get quote
const quoteResponse = await fetch('/v1/quote', {
  method: 'POST',
  headers: { 'X-API-Key': 'YOUR_API_KEY' },
  body: JSON.stringify({
    mode: "eoa",
    ownerAddress: "0x...",
    fundingTokens: [{
      tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      chainId: 1,
      amount: "1000000000"
    }],
    instructions: [...]
  })
});

const { payloadToSign, quote, fee, quoteType, ownerAddress } = await quoteResponse.json();

// 2. Sign each payload (one per funding token)
for (let i = 0; i < payloadToSign.length; i++) {
  const payload = payloadToSign[i];

  // Use the utility function - it handles all types
  const signature = await signQuoteSignablePayload(quoteType, payload);

  // Add signature back to payload
  payloadToSign[i].signature = signature;
}

// 3. Execute with signed payloads
await fetch('/v1/execute', {
  method: 'POST',
  body: JSON.stringify({
    ownerAddress,
    fee,
    quoteType,
    quote,
    payloadToSign
  })
});

Summary

  1. Copy the utilities - They handle all signature types automatically
  2. Get a quote - The API returns the appropriate payload type
  3. Sign with utilities - signQuoteSignablePayload() routes to the right method
  4. Execute - Send signed payloads back to complete the supertransaction
The utilities abstract away the complexity while the API’s clever hash embedding enables powerful single-signature cross-chain operations.

Why EIP-712 Typed Data?

MEE v2.2.1 uses EIP-712 typed data signing for smart-account mode for several benefits:
Wallets display structured data instead of opaque hex strings. Users can see transaction details like amounts, recipients, and deadlines before signing.
Users can verify transaction details in their wallet UI before signing, making it harder for malicious sites to trick users into signing harmful transactions.
Signatures are cryptographically bound to specific contracts and chains, preventing signature replay attacks across different applications or networks.
Structured types prevent signature malleability attacks and ensure data integrity throughout the signing process.

Migration Notes

When migrating from v2.1.0 to v2.2.1 or supporting both versions:
  1. Update signing logic - Use the type guard to handle both payload formats
  2. Test with both formats - Ensure backward compatibility with legacy accounts
  3. No API changes needed - The API automatically returns the correct format based on account version
  4. Upgrade transactions use personal message - When upgrading a v2.1.0 account, the upgrade quote uses personal message signing (not EIP-712)