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

# Wallet and signing

> Connect EVM or Solana wallet adapters while keeping review, approval, and signing under application control.

Wallet adapters let the Agent SDK ask your application to sign or submit a typed Action. Current transaction execution also exposes durable Commit views with their own review and recovery lifecycle. The wallet stays in your application, and your UI keeps the approval boundary.

## Adapt a Viem wallet

Install Viem alongside the Aomi client:

```bash theme={null}
npm install @aomi-labs/client viem
```

```ts theme={null}
import { Aomi, type EvmWallet } from "@aomi-labs/client";
import type {
  Hex,
  SignTypedDataParameters,
  WalletClient,
} from "viem";

function toAomiWallet(wallet: WalletClient): EvmWallet {
  const account = wallet.account;
  if (!account) throw new Error("Connect the wallet first");

  return {
    address: account.address,
    chainId: () => wallet.chain?.id,
    sendTransaction: ({ to, data, value }) =>
      wallet.sendTransaction({
        account,
        chain: wallet.chain,
        to: to as Hex,
        data: data as Hex | undefined,
        value: value === undefined ? undefined : BigInt(value),
      }),
    signMessage: ({ message }) =>
      wallet.signMessage({
        account,
        message: /^0x(?:[0-9a-fA-F]{2})*$/.test(message)
          ? { raw: message as Hex }
          : message,
      }),
    signTypedData: ({ typedData }) =>
      wallet.signTypedData({
        account,
        ...(typedData as Omit<SignTypedDataParameters, "account">),
      }),
    switchChain: (chainId) => wallet.switchChain({ id: chainId }),
  };
}

const aomi = new Aomi({
  baseUrl: process.env.AOMI_BASE_URL!,
  wallet: { evm: toAomiWallet(walletClient) },
});
```

This adapter covers typed Actions, using one transaction at a time because Viem's `sendCalls` returns a bundle identifier while the Action capability expects transaction hashes. It does not implement durable prepared sends or sign-only Commit capabilities. Add only the methods your wallet actually supports.

<Info>
  The examples target the [current source contract](/docs/integrate/client-sdk#check-compatibility). Wallet signing and submission require a compatible adapter, the requested chain, and explicit user approval.
</Info>

## Review an Agent request

Agent execution is manual by default:

```ts theme={null}
const run = aomi.agent.run("Supply 100 USDC to Aave.");

run.on("action", async (action) => {
  if (action.state !== "pending") return;

  const approved = await showApprovalUI(action);

  if (!approved) {
    await run.reject(action.id, "User rejected");
    return;
  }

  if (!run.session.actions.canExecute(action.id)) {
    throw new Error(`No wallet capability can execute ${action.request.type}`);
  }

  await run.session.actions.execute(action.id);
});

await run.result();
```

The `AgentRun` events are `action`, `completed`, and `error`. Simulation details and warnings, when present, are part of `action.request`. Durable Commits are available through `run.session.commits` and session snapshots; they do not emit an `AgentRun` action event.

Your approval UI should show the request kind, chain, sender, targets, values, simulation evidence, and warnings available on the action. `execute()` calls the configured wallet capability and sends its result back to the Agent.

## Durable transaction reviews

This section requires the newer client and a deployment that exposes Commit views. Use a host-authenticated `aomi` client with a first-party session or origin-bound widget credential accepted by `/api/commits/*`. A `/v1/agent` OAuth grant alone does not authorize these host routes. A durable Commit has a stable `commit_id`, a version, a state, review evidence, and its next required wallet action. Refresh it after reconnecting or losing a response; never infer success from a closed wallet popup.

For a custom UI, subscribe to the session and render the controller's current reviews:

```ts theme={null}
import { commitCapabilities } from "@aomi-labs/client";

// wallets implements the required sign/broadcast or prepared-send methods.
// recoveryStore persists CommitRecoveryRecord values per thread and commit.
const run = aomi.agent.run("Prepare the transaction I requested.", {
  commits: commitCapabilities(wallets, recoveryStore),
});

const unsubscribe = run.session.subscribe(() => {
  for (const commit of run.session.getSnapshot().commits) {
    renderCommitReview(commit, run.session.commits.review(commit.commit_id));
  }
});

// Invoke these from explicit UI controls after displaying the latest review.
async function approveCommit(commitId: string) {
  const current = await run.session.commits.refresh(commitId);
  if (!run.session.commits.canExecute(current)) {
    throw new Error("Connect a wallet supporting this Commit action");
  }
  await run.session.commits.execute(commitId);
}

async function rejectCommit(commitId: string) {
  await run.session.commits.reject(commitId);
}

try {
  await run.result();
} finally {
  unsubscribe();
}
```

The controller follows the server's action and validates the expected signer. A sign-only adapter supplies `signTransaction` and, when requested, `broadcastTransaction`. An external EVM wallet using prepared sends needs both `preparePreparedTransaction` and `sendPreparedTransaction`, plus a durable `CommitRecoveryStore`. Forward the complete prepared payload, including fee and nonce fields; do not reconstruct a different transaction from a display summary.

Persist recovery records before opening the wallet and after it returns a transaction hash. An unresolved wallet attempt needs reconciliation, not another send. `submitted` is a Commit lifecycle state, while `confirmed` is its successful terminal state. These states are separate from Pipeline's `status: "committed"` response.

## Review a Pipeline Build

Pipeline keeps review and commit separate:

```ts theme={null}
const build = await aomi.pipeline.evm.stage({
  chainId: 8453,
  calls: [{ to: contract, data: calldata, value: 0n }],
});

const simulated = await build.simulate();

renderReview({
  summary: simulated.summary,
  actions: simulated.actions,
  simulation: simulated.simulation,
});

if (await userApproved()) {
  const result = await simulated.commit();
  renderResult(result);
}
```

Pipeline commit does not automatically use the wallet configured on `Aomi`. It returns `status: "committed"`, `digest`, operation output (`result` for EVM or `results` for SVM), and `requests: ActionRequest[]`. These requests have no durable Agent Action IDs. Your application owns their wallet execution and receipt tracking; `committed` does not mean confirmed on chain.

<Warning>
  Catalog access does not grant execution permission. Use a credential authorized for `pipeline:execute` on your selected environment before testing stage, simulate, build, or commit.
</Warning>

## Keep the boundary explicit

* Never store a private key in browser code or docs examples.
* Do not infer approval from a successful simulation.
* Verify that the active wallet matches the requested sender.
* Ask before switching chains or clusters.
* Treat unattended execution as a separate, explicit product decision.
* Keep guest identity, account authentication, and wallet authority separate.

Read [Transaction safety](/docs/security/transaction-safety) for the broader execution model.
