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

# Contract interactions

> Send single, batched, and read-only contract calls through a Startale smart account.

`StartaleAccountClient` exposes a small set of actions that cover almost every contract interaction you will write. This page shows how to encode calldata, send a single call, batch multiple calls atomically, and handle the most common errors.

<Note>
  Source: [`StartaleGroup/scs-aa-sdk`](https://github.com/StartaleGroup/scs-aa-sdk).
</Note>

## Encode the calldata

Use `encodeFunctionData` from `viem` to turn a function call into ABI-encoded `0x` bytes. The result becomes the `data` field of a `Call` object.

```ts theme={null}
import { encodeFunctionData } from "viem"

const counterAbi = [
  { name: "count", type: "function", stateMutability: "nonpayable", inputs: [], outputs: [] },
] as const

const callData = encodeFunctionData({
  abi: counterAbi,
  functionName: "count",
})
```

| Symbol               | Source | Role                                                  |
| -------------------- | ------ | ----------------------------------------------------- |
| `encodeFunctionData` | `viem` | ABI-encodes a function name + args into hex calldata. |

If you already use viem's `getContract` or `writeContract`, you can reuse those ABI fragments directly here.

## Send a single call

```ts theme={null}
const hash = await smartAccountClient.sendUserOperation({
  calls: [
    {
      to: counterAddress,
      data: callData,
      value: 0n,
    },
  ],
})

const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash })
```

| Symbol                                           | Source                 | Role                                                                                                |
| ------------------------------------------------ | ---------------------- | --------------------------------------------------------------------------------------------------- |
| `smartAccountClient.sendUserOperation`           | `@startale-scs/aa-sdk` | Builds, signs, and submits a UserOperation through the SCS bundler. Returns the UserOperation hash. |
| `smartAccountClient.waitForUserOperationReceipt` | `@startale-scs/aa-sdk` | Polls the bundler until the UserOperation is mined, then returns `{ success, receipt, ... }`.       |

`calls[].value` is in wei. The smart account itself does not need to hold ETH unless you set a non-zero `value` and your paymaster does not cover it.

## Batch multiple calls atomically

A single UserOperation can carry many calls. They are executed in order inside the same EVM transaction; if any one reverts, the whole UserOperation reverts and nothing is settled. This is built in to `sendUserOperation`: just append more entries to the `calls` array.

```ts theme={null}
import type { Address } from "viem"

const calls: { to: Address; data: `0x${string}`; value: bigint }[] = [
  { to: tokenAddress, data: approveCalldata, value: 0n },
  { to: dexAddress, data: swapCalldata, value: 0n },
]

const hash = await smartAccountClient.sendUserOperation({ calls })
const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash })
```

Each entry in `calls` is a `{ to, value, data }` object; ordering is preserved and execution is atomic. A working multi-call example using this exact pattern lives in [`scs-aa-quickstart/src/startale-minato/demo_install_modules.ts`](https://github.com/StartaleGroup/scs-aa-quickstart/blob/main/src/startale-minato/demo_install_modules.ts).

<Tip>
  Batching is the canonical way to fix the "approve then swap" two-transaction UX. The user signs once, the bundler ships one transaction, and gas accounting is single-shot.
</Tip>

## Read state from the same `publicClient`

The smart account does not change how you read onchain data. Reuse the `publicClient` you passed to `createSmartAccountClient`:

```ts theme={null}
const count = await publicClient.readContract({
  abi: counterAbi,
  address: counterAddress,
  functionName: "getCount",
})
```

## Handle errors

`sendUserOperation` throws when the bundler rejects the UserOperation, when the paymaster declines to sponsor it, or when validation reverts. A small triage helper goes a long way:

```ts theme={null}
function explainUserOpError(error: unknown): string {
  const message = error instanceof Error ? error.message : String(error)

  if (message.includes("paymaster")) return "The paymaster declined to sponsor this UserOperation. Check policy limits and `paymasterId`."
  if (message.includes("insufficient funds")) return "The smart account or sponsor lacks funds for gas. Top up or switch to sponsored mode."
  if (message.includes("AA21") || message.includes("AA22")) return "Signature validation failed at the EntryPoint. Check the signer and validator module."
  if (message.includes("execution reverted")) return "A target contract reverted. Inspect the trace for the failing call."
  return `UserOperation failed: ${message}`
}
```

The `AA**` codes come from the EntryPoint contract; their full meaning is documented in the [ERC-4337 spec](https://eips.ethereum.org/EIPS/eip-4337#error-codes).

## Send a transaction-style call

If your code expects the viem `sendTransaction` shape, the smart account client supports it too. It compiles the call into a single-call UserOperation under the hood:

```ts theme={null}
const hash = await smartAccountClient.sendTransaction({
  to: counterAddress,
  data: callData,
  value: 0n,
})
```

The return value is still a UserOperation hash; pass it to `waitForUserOperationReceipt`.

## Next steps

<CardGroup cols={2}>
  <Card title="Sponsored paymaster" icon="gas-pump" href="/aa-sdk/tutorials/sponsored-tx">
    Send the same calls with gas paid by your paymaster.
  </Card>

  <Card title="ERC-20 paymaster" icon="coins" href="/aa-sdk/tutorials/erc20-payment">
    Quote a token, approve it once, and pay gas in tokens.
  </Card>

  <Card title="Parallel transactions" icon="bolt" href="/aa-sdk/advanced/parallel-tx">
    Run independent UserOperations on different nonce lanes.
  </Card>

  <Card title="Smart sessions" icon="key-skeleton" href="/aa-sdk/tutorials/smart-sessions">
    Drop the signature prompt for repeated, scoped calls.
  </Card>
</CardGroup>
