Skip to main content
Tempo features a 2D nonce system that allows multiple transactions to be processed in parallel. Unlike traditional sequential nonces, you can use a nonceKey to create independent transaction lanes.

How It Works

Traditional blockchains use sequential nonces (0, 1, 2, 3…). Tempo supports a 2D nonce system where:
  • Each nonceKey represents an independent transaction lane
  • Transactions within the same lane are sequential
  • Transactions across different lanes can be processed in parallel
This is useful for:
  • Processing multiple independent operations simultaneously
  • Avoiding nonce conflicts in high-throughput applications
  • Better transaction throughput

Example: Parallel Transaction Lanes

import { FC, useState } from 'react';
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isTempoWallet } from '@dynamic-labs/tempo';
import { encodeFunctionData, erc20Abi, Hex, keccak256, toHex } from 'viem';

const ParallelTransactionsButton: FC = () => {
  const { primaryWallet } = useDynamicContext();
  const [txHashes, setTxHashes] = useState<string[]>([]);

  const onSendParallel = async () => {
    if (!primaryWallet || !isTempoWallet(primaryWallet)) {
      throw new Error('Tempo wallet not found');
    }

    const publicClient = await primaryWallet.getPublicClient();
    const walletClient = await primaryWallet.getWalletClient();

    const fromAddress = primaryWallet.address as Hex;
    const tokenAddress = '0xToken...' as Hex;
    const feeTokenAddress = '0xFeeToken...' as Hex;

    // Define multiple recipients for parallel transfers
    const transfers = [
      { to: '0xRecipient1...' as Hex, amount: BigInt(1000000), lane: 'lane-1' },
      { to: '0xRecipient2...' as Hex, amount: BigInt(2000000), lane: 'lane-2' },
      { to: '0xRecipient3...' as Hex, amount: BigInt(3000000), lane: 'lane-3' },
    ];

    const gasPrice = await publicClient.getGasPrice();
    const chainId = await publicClient.getChainId();

    // Create transactions for each lane
    const txPromises = transfers.map(async ({ to, amount, lane }) => {
      // Generate a unique nonceKey for each lane
      const nonceKey = keccak256(toHex(lane));

      // Get nonce for this specific lane
      // Note: You may need to track nonces per lane in your application
      const nonce = await publicClient.getTransactionCount({
        address: fromAddress,
      });

      const callData = encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: [to, amount],
      });

      const tempoTx = {
        calls: [
          {
            data: callData,
            to: tokenAddress,
            value: BigInt(0),
          },
        ],
        chainId,
        gas: BigInt(300000),
        maxFeePerGas: gasPrice,
        maxPriorityFeePerGas: gasPrice / BigInt(10),
        nonce,
        nonceKey, // Use 2D nonce with lane key
        feeToken: feeTokenAddress,
      };

      return walletClient.sendTransaction(tempoTx);
    });

    // Send all transactions in parallel
    const hashes = await Promise.all(txPromises);
    setTxHashes(hashes);

    console.log('Parallel transaction hashes:', hashes);
  };

  return (
    <div>
      <button onClick={onSendParallel}>Send Parallel Transactions</button>
      {txHashes.length > 0 && (
        <ul>
          {txHashes.map((hash, i) => (
            <li key={i}>Tx {i + 1}: {hash}</li>
          ))}
        </ul>
      )}
    </div>
  );
};

Parameters

ParameterTypeDescription
nonceKeyHexA 32-byte key identifying the transaction lane. Transactions with the same nonceKey are sequential; different keys allow parallel processing.
noncenumberThe sequential nonce within the specific lane

Best Practices

  1. Generate unique lane keys: Use descriptive identifiers that you can track (e.g., keccak256(toHex('payments')))
  2. Track nonces per lane: If using multiple lanes, maintain separate nonce counters for each
  3. Use for independent operations: Parallel lanes are best for operations that don’t depend on each other