# Bring your own signer

> Use any viem-compatible signer with Wallet APIs

> For the complete documentation index, see [llms.txt](/docs/llms.txt).

<Info>`@alchemy/wallet-apis` (v5.x.x) is currently in beta but is the recommended replacement for `@account-kit/wallet-client` (v4.x.x). If you run into any issues, please [reach out](mailto:support@alchemy.com).</Info>

Any signer that provides a viem `LocalAccount` or `WalletClient` works with `createSmartWalletClient`. This guide covers the requirements and how to set up each type.

## Requirements

Your signer must implement:

| Property | Required | Purpose |
|---|---|---|
| `address` | Yes | The signer's Ethereum address |
| `signMessage` | Yes | Signs plaintext and raw messages (EIP-191) |
| `signTypedData` | Yes | Signs structured data (EIP-712) |
| `signAuthorization` | Yes (for EIP-7702) | Signs the EIP-7702 delegation authorization |

`signAuthorization` is required for [EIP-7702](/docs/wallets/transactions/using-eip-7702), which is the default mode. If your signer doesn't support it, you can use [non-7702 mode](/docs/wallets/transactions/using-eip-7702#how-to-use-non-7702-mode) instead.

## Option 1: `LocalAccount` (recommended)

Use a `LocalAccount` when your signer gives you direct access to a private key or a signing function. This is the recommended approach because it provides the `signAuthorization` interface needed for EIP-7702 delegation.

```ts
import { toAccount } from "viem/accounts";
import { arbitrumSepolia } from "viem/chains";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

const account = toAccount({
  address: "0xYOUR_SIGNER_ADDRESS",

  async signMessage({ message }) {
    // delegate to your signer
  },

  async signTypedData(typedData) {
    // delegate to your signer
  },

  async signTransaction(transaction) {
    // unused by the Smart Wallet client
    throw new Error("signTransaction is not implemented");
  },

  async signAuthorization(unsignedAuth) {
    // required for EIP-7702 delegation
    // delegate to your signer and return { ...unsignedAuth, r, s, yParity }
  },
});

const client = createSmartWalletClient({
  signer: account,
  transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
  chain: arbitrumSepolia,
});
```

For a private key signer, you can use viem's built-in helper which implements all methods automatically:

```ts
import { privateKeyToAccount } from "viem/accounts";

const client = createSmartWalletClient({
  signer: privateKeyToAccount("0x..."),
  transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
  chain: arbitrumSepolia,
});
```

## Option 2: `WalletClient`

Use a `WalletClient` when your signer is a browser wallet or embedded wallet that exposes an EIP-1193 provider (e.g. `window.ethereum`).

```ts
import { createWalletClient, custom } from "viem";
import { arbitrumSepolia } from "viem/chains";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

// Create a WalletClient from the browser wallet
const [address] = await window.ethereum.request({ method: "eth_requestAccounts" });

const walletClient = createWalletClient({
  account: address,
  chain: arbitrumSepolia,
  transport: custom(window.ethereum),
});

// Pass the WalletClient as the signer
const client = createSmartWalletClient({
  signer: walletClient,
  transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
  chain: arbitrumSepolia,
  paymaster: { policyId: "YOUR_GAS_POLICY_ID" }, // optional
});
```

<Note>
  EIP-7702 authorization signing requires a `LocalAccount` with `signAuthorization` implemented. If your wallet doesn't support this, you may need to use [non-7702 mode](/docs/wallets/transactions/using-eip-7702#how-to-use-non-7702-mode) instead.
</Note>

## Sending transactions

Once you have a client, usage is the same regardless of signer type:

```ts
const result = await client.sendCalls({
  calls: [
    {
      to: "0x0000000000000000000000000000000000000000",
      value: BigInt(0),
      data: "0x",
    },
  ],
});

const txStatus = await client.waitForCallsStatus({
  id: result.id,
  timeout: 60_000,
});

console.log("Transaction confirmed:", txStatus.receipts?.[0]?.transactionHash);
```

## Integrations

See full integration guides for supported providers:

* [Privy](/docs/wallets/third-party/signers/privy)
* [Turnkey](/docs/wallets/third-party/signers/turnkey)
* [Openfort](/docs/wallets/third-party/signers/openfort)