> ## Documentation Index
> Fetch the complete documentation index at: https://hntlabs-feature-bot-sdk-changelog-system.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Onchain Interactions

Towns bots are onchain apps powered by ERC-4337 (Account Abstraction) that can interact with any smart contract using ERC-7821 batch execution.

The bot exposes `viem` client and app address for direct blockchain interactions:

* `bot.viem` - Viem client with your gas wallet account for signing transactions
* `bot.appAddress` - Your bot's treasury wallet address (SimpleAccount)

<Note>
  Your **gas wallet** (`bot.viem.account`) needs Base ETH to pay for gas fees when executing transactions. See [Understanding Your Bot's Wallet Architecture](/build/bots/getting-started#understanding-your-bots-wallet-architecture) for details.
</Note>

## Configuration

### Base RPC URL

Bots that interact with smart contracts require a reliable RPC endpoint for Base chain. The default public RPC (`mainnet.base.org`) has strict rate limits and will cause your bot to fail quickly under normal usage.

You can configure a custom RPC URL by passing `baseRpcUrl` to `makeTownsBot()`:

```typescript theme={null}
const bot = await makeTownsBot(
  process.env.APP_PRIVATE_DATA!,
  process.env.JWT_SECRET!,
  {
    baseRpcUrl: process.env.BASE_RPC_URL,
    commands
  }
)
```

Add the RPC URL to your `.env` file:

```bash theme={null}
BASE_RPC_URL=https://base-mainnet.g.alchemy.com/v2/YOUR_API_KEY
```

We recommend getting a RPC URL from a reputable provider, like [Alchemy](https://www.alchemy.com/).

## Reading from Contracts

Read from any contract without making a transaction:

```ts theme={null}
import { readContract } from 'viem/actions'
import simpleAppAbi from '@towns-protocol/bot/simpleAppAbi'

const owner = await readContract(bot.viem, {
  address: bot.appAddress,
  abi: simpleAppAbi,
  functionName: 'moduleOwner',
  args: []
})
```

## Writing to Contracts

### execute (Primary Method)

Use `execute` from ERC-7821 for any onchain interaction. This is your main tool for blockchain operations.

**Single Operation:**

```ts theme={null}
import { execute } from 'viem/experimental/erc7821'
import { parseEther } from 'viem'
import { waitForTransactionReceipt } from 'viem/actions'

bot.onSlashCommand('transfer', async (handler, { channelId, mentions, args }) => {
  const recipient = mentions[0]?.userId
  const amount = parseEther(args[0])

  const hash = await execute(bot.viem, {
    address: bot.appAddress,
    account: bot.viem.account,
    calls: [{
      to: tokenAddress,
      abi: erc20Abi,
      functionName: 'transfer',
      args: [recipient, amount]
    }]
  })

  await waitForTransactionReceipt(bot.viem, { hash })
  await handler.sendMessage(channelId, `Transferred! Tx: ${hash}`)
})
```

**Batch Operations:**

Execute multiple operations atomically (all succeed or all fail):

```ts theme={null}
import { execute } from 'viem/experimental/erc7821'
import { parseEther } from 'viem'
import { waitForTransactionReceipt } from 'viem/actions'

bot.onSlashCommand('defi', async (handler, { channelId, args }) => {
  const amount = parseEther(args[0])

  const hash = await execute(bot.viem, {
    address: bot.appAddress,
    account: bot.viem.account,
    calls: [
      {
        to: tokenAddress,
        abi: erc20Abi,
        functionName: 'approve',
        args: [dexAddress, amount]
      },
      {
        to: dexAddress,
        abi: dexAbi,
        functionName: 'swap',
        args: [tokenIn, tokenOut, amount]
      },
      {
        to: stakingAddress,
        abi: stakingAbi,
        functionName: 'stake',
        args: [amount]
      }
    ]
  })

  await waitForTransactionReceipt(bot.viem, { hash })
  await handler.sendMessage(channelId, `Swapped and staked! Tx: ${hash}`)
})
```

### executeBatch

For advanced use cases requiring batches of batches:

```ts theme={null}
import { executeBatch } from 'viem/experimental/erc7821'

const hash = await executeBatch(bot.viem, {
  address: bot.appAddress,
  account: bot.viem.account,
  calls: [
    [/* first batch */],
    [/* second batch */],
    [/* third batch */]
  ]
})
```

### writeContract

Use **only** for your bot's SimpleAccount contract operations:

```ts theme={null}
import { writeContract, waitForTransactionReceipt } from 'viem/actions'
import simpleAppAbi from '@towns-protocol/bot/simpleAppAbi'
import { parseEther, zeroAddress } from 'viem'

const hash = await writeContract(bot.viem, {
  address: bot.appAddress,
  abi: simpleAppAbi,
  functionName: 'sendCurrency',
  args: [recipientAddress, zeroAddress, parseEther('0.01')]
})

await waitForTransactionReceipt(bot.viem, { hash })
```

## Utility Functions

### getSmartAccountFromUserId

Get a user's smart account address from their Towns user ID.

```ts theme={null}
import { getSmartAccountFromUserId } from '@towns-protocol/bot'

bot.onSlashCommand('wallet', async (handler, event) => {
  const wallet = await getSmartAccountFromUserId(bot, {
    userId: event.userId
  })

  if (wallet) {
    await handler.sendMessage(event.channelId, `Your wallet: ${wallet}`)
  } else {
    await handler.sendMessage(event.channelId, 'No smart account found')
  }
})
```

Returns `null` if no smart account exists.

**Use cases:**

* Send tokens/NFTs to users
* Check on-chain balances
* Airdrop rewards
* Verify asset ownership

## When to Use Each Method

* **`readContract`** - Read from any contract (no transaction, no gas)
* **`writeContract`** - Bot's SimpleAccount contract operations only
* **`execute`** - Primary method for any onchain interaction
  * Tips, swaps, staking, NFT minting, DeFi
  * Single operations or batch operations
  * Atomic execution (all succeed or all fail)
  * Gas optimized for batches
* **`executeBatch`** - Advanced batching (batches of batches)
