Skip to main content

Batching Transactions

CreateBatchTransaction submits several on-chain actions as one transaction. Instead of sending each action separately — paying a separate fee and tracking a separate Transaction

for each — you pass a list of actions and the platform bundles them into a single batched extrinsic.

One batch, one signer

A batch is a single extrinsic, so every action in it is signed by one account. You can't, for example, burn one player's tokens and mint from the game's collection in the same batch — those need two different signers. Set the signer for the whole batch with signerExternalId / signerAddress (see Signing as a managed wallet), or leave it to the Wallet Daemon.

Example: crafting an item

Crafting is a good example of why batching matters. To craft a sword, a player's recipe consumes several ingredients at once — say 3 Wood, 2 Iron, and 1 Blueprint. You Burn

all three from the player's wallet in one batch.

The important part is what happens on failure. If you sent three separate burns and the player turned out to be one Iron short, the Wood and Blueprint would already be gone — the player loses ingredients and gets nothing. Batching makes the whole thing all-or-nothing: if any single burn can't go through, the entire batch reverts and none of the ingredients are consumed.

That behaviour is the default. batchMode defaults to ALL_OR_NOTHING, so the example below doesn't set it — the three burns either all succeed or all revert together.

Because the ingredients live in the player's wallet, the batch is signed by that player's managed wallet via signerExternalId.

mutation CraftItem($signerExternalId: String!, $transactions: [TransactionInput!]!) {
CreateBatchTransaction(
network: CANARY # or ENJIN for mainnet
chain: MATRIX
signerExternalId: $signerExternalId # the player's managed wallet signs the whole batch
transactions: $transactions
# batchMode omitted → defaults to ALL_OR_NOTHING: the batch reverts entirely if any burn fails
) {
uuid
action
state
}
}

Variables:

{
"signerExternalId": "docs-example-player",
"transactions": [
{ "burnToken": { "collectionId": 36105, "tokenId": 1, "amount": 3 } },
{ "burnToken": { "collectionId": 36105, "tokenId": 2, "amount": 2 } },
{ "burnToken": { "collectionId": 36105, "tokenId": 3, "amount": 1 } }
]
}
Minting the crafted item is a separate transaction

Handing the player their new sword means minting it, and minting can only be signed by the collection owner (your game) — a different signer than the player's managed wallet that burned the ingredients. So the mint can't live in the same batch as the burns; run it as its own game-signed CreateTransaction once the burn batch reaches FINALIZED. Pass an idempotencyKey on the mint so a retry never mints the reward twice.

Batch modes

batchMode controls what happens when one of the calls in a batch fails. It takes one of three values and defaults to ALL_OR_NOTHING.

ModeOn-chain callBehaviour on failureUse it for
ALL_OR_NOTHING (default)Utility.batch_allAny failure reverts the entire batch — it's atomic.Crafting, atomic swaps — anything where a partial result is unacceptable.
HALT_ON_ERRORUtility.batchRuns the calls in order and stops at the first failure. Calls that already executed are kept, not reverted.Ordered steps where later calls depend on earlier ones and there's no harm in the completed prefix.
CONTINUE_ON_ERRORUtility.force_batchRuns every call and simply skips the ones that fail.Bulk, independent actions where one bad entry shouldn't block the rest.
HALT_ON_ERROR and CONTINUE_ON_ERROR are not atomic

Only ALL_OR_NOTHING reverts completed work. With the other two modes, some calls can succeed while others fail, leaving the batch partially applied. Inspect the transaction's events once it's FINALIZED to see exactly which calls went through. See Working with Events.

Example: sweeping multiple listings with CONTINUE_ON_ERROR

A player wants to buy several marketplace listings at once. Listings are a shared resource — between the moment the player loads their cart and the moment the batch lands, another buyer might fill one or the seller might cancel it. With CONTINUE_ON_ERROR the batch fills every listing that's still available and skips the ones that are gone, instead of failing the whole purchase over a single unavailable item.

mutation SweepListings($signerExternalId: String!, $transactions: [TransactionInput!]!) {
CreateBatchTransaction(
network: CANARY # or ENJIN for mainnet
chain: MATRIX
signerExternalId: $signerExternalId # the buyer's managed wallet signs
batchMode: CONTINUE_ON_ERROR # fill whatever's still available; skip listings already sold or cancelled
transactions: $transactions
) {
uuid
action
state
}
}

Variables:

{
"signerExternalId": "docs-example-player",
"transactions": [
{ "fillListing": { "id": "0xListingIdA", "amount": 1 } },
{ "fillListing": { "id": "0xListingIdB", "amount": 1 } },
{ "fillListing": { "id": "0xListingIdC", "amount": 1 } }
]
}
Explore More Arguments

For a comprehensive view of all available arguments for queries and mutations, please refer to our API Reference, or the CreateBatchTransaction reference. This resource will guide you on how to use the GraphiQL Playground to explore the full structure and functionality of our API.