Skip to content

How to use your own Account Signer

Account Kit is designed to be flexible and allow you to use any Signer you want. If you don't want to use any Signer implementations in aa-signers, you can either:

  1. Implement SmartAccountSigner (exported in aa-core).
  2. If your Signer is an EIP-1193 compliant provider, you can leverage viem's WalletClient and the WalletClientSigner (exported in aa-core).

Note

If you want to add your Signer implementation to Account Kit's codebase, take a look at the contibuting docs. We welcome Pull Requests onto the Github repo for aa-sdk!

1. Implementing SmartAccountAuthenticator or SmartAccountSigner

Smart accounts in Account Kit expect an implementation of SmartAccountSigner to work in Account Kit. We also include a SmartAccountAuthenticator interface that extends SmartAccountSigner and wraps any SDKs you may wish to use as part of the implementation of your own Signer.

ts
import type { Address } from "abitype";
import type {
  Hex,
  SignableMessage,
  TypedData,
  TypedDataDefinition,
} from "viem";

//#region SmartAccountAuthenticator
/**
 * Extends the @interface SmartAccountSigner interface with authentication.
 *
 * @template AuthParams - the generic type of the authentication parameters
 * @template AuthDetails - the generic type of the authentication details
 * @template Inner - the generic type of the inner client that the signer wraps to provide functionality such as signing, etc.
 *
 * @method authenticate - authenticate the signer
 * @method getAuthDetails - get the authentication details
 */
export interface SmartAccountAuthenticator<AuthParams, AuthDetails, Inner = any>
  extends SmartAccountSigner<Inner> {
  authenticate: (params: AuthParams) => Promise<AuthDetails>;

  getAuthDetails: () => Promise<AuthDetails>;
}
//#endregion SmartAccountAuthenticator

//#region SmartAccountSigner
/**
 * A signer that can sign messages and typed data.
 *
 * @template Inner - the generic type of the inner client that the signer wraps to provide functionality such as signing, etc.
 *
 * @var signerType - the type of the signer (e.g. local, hardware, etc.)
 * @var inner - the inner client of @type {Inner}
 *
 * @method getAddress - get the address of the signer
 * @method signMessage - sign a message
 * @method signTypedData - sign typed data
 */
export interface SmartAccountSigner<Inner = any> {
  signerType: string;
  inner: Inner;

  getAddress: () => Promise<Address>;

  signMessage: (message: SignableMessage) => Promise<Hex>;

  signTypedData: <
    const TTypedData extends TypedData | { [key: string]: unknown },
    TPrimaryType extends string = string
  >(
    params: TypedDataDefinition<TTypedData, TPrimaryType>
  ) => Promise<Hex>;
}
//#endregion SmartAccountSigner

2. Using WalletClientSigner

Viem allows you to create a WalletClient, which can be used to wrap local or JSON RPC based wallets. You can see the complete docs for leveraging the WalletClient here.

We support a SmartAccountSigner implementation called WalletClientSigner that makes it really easy to use a viem WalletClient as a signer on your Smart Contract Account. If your Signer is EIP-1193 compliant, it is really easy to use with WalletClient. Let's take a look at a simple example:

ts
import {
  WalletClientSigner,
  sepolia,
  type SmartAccountSigner,
} from "@alchemy/aa-core";
import { createWalletClient, custom } from "viem";

const externalProvider = window.ethereum; // or anyother EIP-1193 provider

const walletClient = createWalletClient({
  chain: sepolia, // can provide a different chain here
  transport: custom(externalProvider),
});

export const signer: SmartAccountSigner = new WalletClientSigner(
  walletClient,
  "json-rpc" // signerType
);