> ## Documentation Index
> Fetch the complete documentation index at: https://docs.illa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling Tool Execution

> Execute pending tools and return outcomes to complete the chat loop

When ILLA needs your application to execute an on-chain action, the response includes `pendingTools`. Your app becomes the executor: run those tools, collect the result, and send the outcome back with `sendToolResults`.

## What this guide covers

* how to read `pendingTools`
* how to run the standard execution loop
* how to handle `ActionSequence` and signature requests
* how to use simulation previews safely
* when to use polling helpers

## Read `pendingTools`

Each item in `pendingTools` includes:

* `toolCallId`
* `toolName`
* `input`
* `simulated` (optional)
* `simulation` (optional simulation preview payload)

`input.type` can be:

* `SingleTransaction`
* `BatchTransactions`
* `SignatureRequests`
* `ActionSequence`

`tool.input` is always the execution source of truth. Optional simulation fields are for display and review.

## Run the standard execution loop

Use batched outcome submission (`sendToolResults`) and continue until no more tools are returned.

```ts theme={null}
import { IllaSDK } from '@illalabs/sdk'
import {
  IllaToolError,
  IllaToolOutcome,
  type IllaToolOutcomeJSON,
  type PendingToolCallType,
} from '@illalabs/interfaces'

const sdk = new IllaSDK({ apiKey: process.env.ILLA_API_KEY! })

async function executePendingTool(tool: PendingToolCallType): Promise<IllaToolOutcomeJSON> {
  try {
    // Your executor should handle:
    // SingleTransaction / BatchTransactions / SignatureRequests / ActionSequence
    const executionResult = await walletExecutor.execute(tool.input)

    return IllaToolOutcome
      .success(tool.toolCallId, tool.toolName, executionResult)
      .toJSON()
  } catch (error) {
    return IllaToolOutcome
      .error(
        tool.toolCallId,
        tool.toolName,
        IllaToolError.execution(
          tool.toolCallId,
          tool.toolName,
          error instanceof Error ? error.message : 'Unknown execution error',
        ),
      )
      .toJSON()
  }
}

async function runToolLoop(
  chatId: string,
  initialTools: ReadonlyArray<PendingToolCallType>,
): Promise<void> {
  let pendingTools = [...initialTools]

  while (pendingTools.length > 0) {
    const outcomes = await Promise.all(pendingTools.map(executePendingTool))

    const followUp = await sdk.sendToolResults(chatId, outcomes)
    if (followUp.response.isError) {
      throw new Error(followUp.response.error.message)
    }

    pendingTools = [...(followUp.response.data.pendingTools ?? [])]
  }
}
```

This is the default pattern for SDK integrations.

## Handle `ActionSequence`

When `input.type` is `ActionSequence`, the value is an ordered array of steps. Execute steps sequentially and preserve the server-defined order.

```ts theme={null}
async function executeActionSequence(steps: ActionStep[]): Promise<unknown[]> {
  const results = []

  for (const step of steps) {
    switch (step.type) {
      case 'SingleTransaction':
        results.push(await sendTransaction(step.value.transaction))
        break
      case 'BatchTransactions':
        results.push(await sendBatchTransactions(step.value))
        break
      case 'SignatureRequests':
        results.push(await collectSignatures(step.value.signatureRequests))
        break
    }
  }

  return results
}
```

A common pattern is a prediction-market flow that transfers funds first and collects a signature second.

## Handle signature requests

Some tools return `SignatureRequests` directly, or as a step inside `ActionSequence`. Signature requests use either EIP-712 typed data (`signMethod: 'typedData'`) or raw hash signing (`signMethod: 'message'`).

Your executor should collect signatures and return them in the tool result payload, then continue the loop normally.

Common flows include:

* `predictionMarketsBet`
* `predictionMarketsRedeem`
* `polymarketPostOrder`
* `polymarketPostRedeem`

## Use simulation previews for review UI

When simulation is enabled server-side, pending tools may include a `simulation` object that you can use to build a confirmation UI before execution.

```ts theme={null}
for (const tool of pendingTools) {
  if (!tool.simulation) continue

  const sim = tool.simulation

  if (!sim.willSucceed) {
    console.warn('Simulation predicts failure:', sim.errorExplanation)
  }

  for (const change of sim.assetChanges) {
    const sign = change.direction === 'out' ? '-' : '+'
    console.log(`${sign}${change.formattedAmount} ${change.tokenSymbol} ($${change.usdValue})`)
  }

  console.log(
    `Gas: ${sim.gasEstimate.gasCostNative} ${sim.nativeTokenSymbol ?? 'ETH'} ($${sim.gasEstimate.gasCostUsd})`,
  )
}
```

Key fields worth surfacing:

* `willSucceed`
* `assetChanges`
* `gasEstimate`
* `actionType`
* `contractInteractions`
* `actionMetadata`

Treat simulation as display-only metadata. `tool.input` remains the execution source of truth.

## End-to-end example

```ts theme={null}
const first = await sdk.sendMessage(
  'Swap 1 ETH to USDC on Base',
  { userContext: { address: '0x1234...' } },
)

if (first.response.isError) {
  throw new Error(first.response.error.message)
}

await runToolLoop(first.chatId, first.response.data.pendingTools ?? [])

const finalMessages = await sdk.getMessages(first.chatId)
console.log(finalMessages)
```

## Track long-running tools

For tools that may take longer after submission (for example bridge completion), use polling helpers with the transaction hash:

```ts theme={null}
const subscription = sdk.subscribeToToolStatus(
  {
    toolName: 'bridge',
    protocol: 'lifi',
    descriptor: { txHash: '0x...' },
  },
  {
    onStatusChange: ({ current }) => console.log(current.pollStatus),
    onError: (error) => console.error(error),
  },
  {
    interval: 5,
    retryThreshold: 30,
    maxRetries: 5,
    maxDuration: 600,
  },
)

subscription.unsubscribe()
```

## Common mistakes

1. Ignoring failed tools instead of returning an error outcome.
2. Executing `ActionSequence` steps out of order.
3. Using simulation output as the execution payload.
4. Sending many single tool results when one batched call would work.

## Related pages

* [ILLA SDK API](/sdk/api/illa-sdk)
* [Simulation previews](/sdk/guides/simulation-previews)
* [Types and utilities](/sdk/api/types-and-utilities)
* [Awaiting actions & polling](/sdk/guides/awaiting-actions-and-polling)
* [Error handling](/sdk/concepts/error-handling)
