Skip to main content

Parallel Transactions with Smart Accounts

With smart accounts, the nonce can be two-dimensional:

  • nonceKey (or nonce space) identifies a logical lane or stream.
  • nonce within that key auto-increments on every UserOperation.

This enables parallel execution across different nonce keys, and sequential ordering within each key.


Visual Illustration

            ┌────────────────────┐
nonceKey 1: │ UserOp A (nonce: 0)│
│ UserOp B (nonce: 1)│
│ UserOp C (nonce: 2)│ ← Sequential
└────────────────────┘

┌────────────────────┐
nonceKey 2: │ UserOp X (nonce: 0)│
│ UserOp Y (nonce: 1)│ ← Independent
└────────────────────┘

┌────────────────────┐
nonceKey 3: │ UserOp Z (nonce: 0)│ ← Parallel
└────────────────────┘

Using nonceKey for Parallel and Sequential Execution

Each nonceKey runs in its own lane — parallel to other keys. Within the same nonceKey, the nonce auto-increments to preserve sequential ordering.


1. Create Smart Account Client

const smartAccountClient = createSmartAccountClient({
account: await toStartaleSmartAccount({
signer: signer,
chain,
transport: http(),
index: BigInt(106910),
}),
transport: http(bundlerUrl),
client: publicClient,
paymaster: scsPaymasterClient,
paymasterContext: scsContext,
});

2. Generate nonces with different keys

Use arbitrary numbers, user-based hashes, or string-based IDs to define your nonceKey.

const myNonce1 = await smartAccountClient.account.getNonce({
key: 1n, // lane 1
});

const myNonce2 = await smartAccountClient.account.getNonce({
key: 2n, // lane 2
});

3. Send UserOps with Different Nonce Keys

const hash1 = smartAccountClient.sendUserOperation({
calls: [...],
nonce: myNonce1, // from key: 1
});

const hash2 = smartAccountClient.sendUserOperation({
calls: [...],
nonce: myNonce2, // from key: 2
});
Result

Result

  • UserOp Anonce key: 1, nonce: 0
  • UserOp Bnonce key: 2, nonce: 0

These will execute in parallel, even if submitted at the same time.