> ## Documentation Index
> Fetch the complete documentation index at: https://aomilabs-victor-docs-redirect-build-overview.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Non-Custodial Automation

> How Aomi keeps funds safe with local key control, Anvil-forked transaction simulation before signing, and explicit per-transaction user approval.

Aomi is **non-custodial**: it never holds your private keys or funds. You remain in full control of your wallet at all times. Every transaction is simulated on a forked network before it reaches your wallet, so you can review the exact onchain outcome before approving.

<iframe className="aspect-video w-full rounded-lg border" src="https://www.youtube.com/embed/TqCiJ7m9ksY" title="Transact with Aomi - connect wallet to sign" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen />

## How Signing Works

Wallet connection and account management run through [Para](https://www.getpara.com). Transaction signing still happens locally in your connected wallet through wagmi-compatible connectors. Aomi constructs and proposes transactions; your wallet signs them. The private key never leaves your device.

```
Aomi Agent → constructs tx → simulates → presents result → your wallet signs
```

## Transaction Simulation: The Safety Guardrail

Before any transaction reaches your wallet for approval, Aomi **simulates it on a forked network**. This is a deliberate guardrail for AI-generated transactions. The LLM cannot send anything to your wallet without first passing through simulation.

### What simulation does

1. Aomi forks the current network state using [Anvil](https://book.getfoundry.sh/anvil/)
2. The transaction is executed against the fork, not the real chain
3. The exact outcome is captured: token balance changes, gas cost, contract calls made
4. The result is shown to you before you're asked to sign

### What you'll see

Before your wallet prompt appears, you'll see a simulation result showing:

* **Tokens sent / received**: exact amounts and token addresses
* **Gas estimate**: expected cost in ETH
* **Contracts called**: which protocols are involved
* **Net balance change**: what your wallet will look like after the transaction

Only after you review and confirm the simulation result does Aomi send the transaction to your wallet for signing.

### Why this matters

LLMs can generate incorrect transaction parameters. Simulation catches problems like wrong amounts, unexpected contract calls, and reverts before they become irreversible onchain. You always have the final say.

## Supported Chains

Simulation and transaction execution is supported on:

* Ethereum Mainnet
* Base
* Arbitrum One
* Major testnets (Sepolia, etc.)

## Wallet Integration

Aomi widgets use the Para + wagmi provider tree directly. The registry install gives you the wallet UI plus `RuntimeTxHandler`; you provide the Para and wagmi providers in your host app.

### Setup

#### Install Dependencies

If you installed the widget through `npx shadcn add https://aomi.dev/r/aomi-frame.json`, the wallet UI is already included. Use the setup below when you want to wire Para-based auth and external wallets in your host app.

```bash theme={null}
npm install @getpara/react-sdk @getpara/evm-wallet-connectors wagmi viem @tanstack/react-query
```

#### Configure Para

```tsx theme={null}
"use client";

import "@getpara/react-sdk/styles.css";
import { ParaProvider, Environment } from "@getpara/react-sdk";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { defineChain, http } from "viem";
import { mainnet, polygon, arbitrum, base } from "wagmi/chains";

const queryClient = new QueryClient();
const walletConnectProjectId =
  process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID ??
  process.env.NEXT_PUBLIC_PROJECT_ID;

const localhost = defineChain({
  id: 31337,
  name: "Localhost",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: {
    default: {
      http: ["http://127.0.0.1:8545"],
    },
  },
});

const useAnvilForWallet = process.env.NEXT_PUBLIC_ANVIL_FOR_WALLET === "true";
const networks = useAnvilForWallet
  ? [localhost, mainnet, polygon, arbitrum, base]
  : [mainnet, polygon, arbitrum, base];

const transports = Object.fromEntries(
  networks.map((chain) => [chain.id, http(chain.rpcUrls.default.http[0])]),
);

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      <ParaProvider
        paraClientConfig={{
          apiKey: process.env.NEXT_PUBLIC_PARA_API_KEY!,
          env:
            (process.env.NEXT_PUBLIC_PARA_ENVIRONMENT as
              | Environment
              | undefined) ?? Environment.BETA,
        }}
        config={{ appName: "Your App" }}
        paraModalConfig={{
          disableEmailLogin: true,
          oAuthMethods: ["GOOGLE"],
        }}
        externalWalletConfig={{
          appDescription: "Your app description",
          appUrl: "https://your-app.com",
          wallets: [
            "WALLETCONNECT",
            "METAMASK",
            "COINBASE",
            "RAINBOW",
            "RABBY",
          ],
          walletConnect: {
            projectId: walletConnectProjectId!,
          },
          evmConnector: {
            config: {
              chains: networks,
              transports,
              ssr: true,
            },
          },
        }}
      >
        {children}
      </ParaProvider>
    </QueryClientProvider>
  );
}
```

### Supported Chains

Configure which chains are available by passing them to Para's EVM connector:

```tsx theme={null}
import { mainnet, polygon, arbitrum, base, optimism } from "wagmi/chains";

externalWalletConfig={{
  evmConnector: {
    config: {
      chains: [mainnet, polygon, arbitrum, base, optimism],
      transports: {
        [mainnet.id]: http(),
        [polygon.id]: http(),
        [arbitrum.id]: http(),
        [base.id]: http(),
        [optimism.id]: http(),
      },
    },
  },
}}
```

The `NetworkSelect` component in the widget (or `getChainInfo` / `SUPPORTED_CHAINS` in the headless library) will reflect the configured chains.

### Transaction Signing Flow

When the AI assistant determines a transaction is needed, the following flow occurs:

```
1. LLM emits wallet_tx_request tool call
2. Backend sends wallet_tx_request event via SSE
3. Frontend receives event via useWalletHandler
4. UI prompts user to review and sign
5. User signs with their wallet (wagmi)
6. Frontend sends wallet:tx_complete event back to backend
7. Backend injects transaction result into LLM context
8. LLM continues the conversation with the result
```

#### In the Widget

The widget includes `RuntimeTxHandler` automatically. When a transaction request arrives, it executes through wagmi inside your Para provider tree and prompts the user to sign.

#### In the Headless Library

Handle transaction requests manually:

```tsx theme={null}
import { useWalletHandler, useAomiRuntime } from "@aomi-labs/react";
import { useSendTransaction, useSignTypedData } from "wagmi";

function WalletRequestHandler() {
  const { currentThreadId } = useAomiRuntime();
  const { pendingRequests, startProcessing, resolveRequest, rejectRequest } =
    useWalletHandler({ sessionId: currentThreadId });

  const { sendTransactionAsync } = useSendTransaction();
  const { signTypedDataAsync } = useSignTypedData();

  const handleRequest = async (request: WalletRequest) => {
    startProcessing(request.id);

    try {
      if (request.kind === "transaction") {
        const payload = request.payload as WalletTxPayload;
        const hash = await sendTransactionAsync({
          to: payload.to,
          value: BigInt(payload.value ?? "0"),
          data: payload.data,
        });
        resolveRequest(request.id, { txHash: hash });
      }

      if (request.kind === "eip712_sign") {
        const payload = request.payload as WalletEip712Payload;
        const signature = await signTypedDataAsync(payload.typedData);
        resolveRequest(request.id, { signature });
      }
    } catch (error) {
      rejectRequest(request.id, error.message);
    }
  };

  return (
    <div>
      {pendingRequests.map((req) => (
        <div key={req.id}>
          <p>Transaction request: {req.kind}</p>
          <button onClick={() => handleRequest(req)}>Approve</button>
          <button onClick={() => rejectRequest(req.id, "User rejected")}>
            Reject
          </button>
        </div>
      ))}
    </div>
  );
}
```

## EIP-712 Typed Data Signing

The AI can also request EIP-712 typed data signatures (used for gasless transactions, permit approvals, etc.). The flow is identical to transaction signing but uses `signTypedData` instead of `sendTransaction`.

## Network Switching

### In the Widget

The `NetworkSelect` component provides a dropdown for switching chains. When the user selects a network, it calls `useSwitchChain` from wagmi and updates the Aomi runtime.

### Programmatically

```tsx theme={null}
import { useSwitchChain } from "wagmi";
import { useUser } from "@aomi-labs/react";

function NetworkSwitcher() {
  const { switchChain } = useSwitchChain();
  const { setUser } = useUser();

  const handleSwitch = async (chainId: number) => {
    await switchChain({ chainId });
    // The widget auth bridge auto-syncs, but you can also set manually:
    setUser({ chainId });
  };
}
```

## ConnectButton Component (Widget)

The widget's `ConnectButton` component handles the full lifecycle:

1. Renders a connect/manage account button
2. On connect: opens the Para auth modal
3. On successful connection: syncs address, chainId, and connection status to `useUser`
4. When already connected: opens the Para account modal

```tsx theme={null}
import { ConnectButton } from "@/components/control-bar";

<ConnectButton
  connectLabel="Connect Wallet"
  onConnectionChange={(isConnected) => {
    console.log("Wallet connected:", isConnected);
  }}
/>;
```

## Utilities

The headless library provides wallet-related utilities:

```tsx theme={null}
import {
  formatAddress,
  getNetworkName,
  getChainInfo,
  SUPPORTED_CHAINS,
} from "@aomi-labs/react";

formatAddress("0x1234567890abcdef1234567890abcdef12345678");
// "0x1234...5678"

getNetworkName(1);
// "Ethereum"

getChainInfo(137);
// { name: "Polygon", ticker: "POL", ... }

SUPPORTED_CHAINS;
// [{ chainId: 1, name: "Ethereum", ... }, ...]
```

## Simulation Details

Simulation runs on a forked network using [Anvil](https://book.getfoundry.sh/anvil/):

* No gas costs for simulation
* No state changes on the real network
* Exact output visible before signing
* Multiple transactions can be simulated in batch
* Error detection before execution

## Account Abstraction

Account abstraction is supported for session keys, gas sponsorship, and batched operations.

## Next Steps

* [Agentic Application](/concepts/what-is-aomi): understand Apps and API keys
* [Simulation Reference](/reference/simulation): technical deep-dive on the simulation pipeline
* [Account Abstraction](/reference/account-abstraction): session keys and gas sponsorship
