Connect external wallets

Overview

Connectors let users authenticate with existing external wallets. We support both EVM (e.g., MetaMask, Coinbase, WalletConnect) and Solana (e.g., Phantom) wallets via UI components or custom UI. and can surface them together in your auth modal.

  • EVM EOAs behave as regular wallets (no smart wallet features like sponsorship).
  • You can optionally use an EVM EOA as a smart wallet owner to unlock smart wallet features like sponsorship and batching.
  • Solana wallets are external wallets; you can enable sponsored gas with a policy.

Pre-built UI connectors

  • Auto-detect browser installed wallet extensions.
  • Optionally add WalletConnect for other EVM wallets.
  • Configure once with configForExternalWallets() and pass into the UI components using createConfig().

You can fully customize wallet connector UI to define features wallets, ordering, and more. See how here.

Detect and display EVM + Solana wallets

Use the helper to generate EVM connectors, Solana adapters, and UI customization in one place. Add to UI components by updating your createConfig.

import {
  
const createConfig: (props: CreateConfigProps, ui?: AlchemyAccountsUIConfig) => AlchemyAccountsConfigWithUI

Wraps the createConfig that is exported from @aa-sdk/core to allow passing an additional argument, the configuration object for the Auth Components UI (the modal and AuthCard).

createConfig
,
const cookieStorage: (config?: { sessionLength?: number; domain?: string; }) => Storage

Function to create cookie based Storage

cookieStorage
,
const configForExternalWallets: ({ wallets, chainType, walletConnectProjectId, moreButtonText, hideMoreButton, numFeaturedWallets, }: ConfigForExternalWalletsParams) => ConfigForExternalWalletsResult

Configure external wallets for Account Kit with simplified wallet name-based configuration Handles both EVM connectors and Solana adapters, returns config for core and UI

configForExternalWallets
,
} from "@account-kit/react"; import {
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
,
const sepolia: Chain
sepolia
} from "@account-kit/infra";
import {
class Connection

A connection to a fullnode JSON RPC endpoint

Connection
} from "@solana/web3.js";
// Keep external wallets settings in one place export const
const externalWalletsConfig: ConfigForExternalWalletsResult
externalWalletsConfig
=
function configForExternalWallets({ wallets, chainType, walletConnectProjectId, moreButtonText, hideMoreButton, numFeaturedWallets, }: ConfigForExternalWalletsParams): ConfigForExternalWalletsResult

Configure external wallets for Account Kit with simplified wallet name-based configuration Handles both EVM connectors and Solana adapters, returns config for core and UI

configForExternalWallets
({
// Preferred order (case-insensitive). Use "wallet_connect" for WalletConnect.
ConfigForExternalWalletsParams.wallets: string[]
wallets
: ["wallet_connect", "phantom", "metamask", "coinbase wallet"],
// Surface both EVM and Solana wallets (filter to ["evm"] or ["svm"] if desired)
ConfigForExternalWalletsParams.chainType: ChainType[]
chainType
: ["svm", "evm"],
// EVM-only WalletConnect support (omit to disable)
ConfigForExternalWalletsParams.walletConnectProjectId?: string | undefined
walletConnectProjectId
: "your-project-id",
// Controls the built-in Featured section
ConfigForExternalWalletsParams.hideMoreButton?: boolean | undefined
hideMoreButton
: false,
ConfigForExternalWalletsParams.numFeaturedWallets?: number | undefined
numFeaturedWallets
: 4,
}); export const
const config: AlchemyAccountsConfigWithUI
config
=
function createConfig(props: CreateConfigProps, ui?: AlchemyAccountsUIConfig): AlchemyAccountsConfigWithUI

Wraps the createConfig that is exported from @aa-sdk/core to allow passing an additional argument, the configuration object for the Auth Components UI (the modal and AuthCard).

createConfig
(
{
transport: AlchemyTransport
transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "your_api_key" }),
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
ssr?: boolean | undefined

Enable this parameter if you are using the config in an SSR setting (eg. NextJS) Turing this setting on will disable automatic hydration of the client store

ssr
: true,
storage?: CreateStorageFn | undefined
storage
:
const cookieStorage: (config?: { sessionLength?: number; domain?: string; }) => Storage

Function to create cookie based Storage

cookieStorage
,
enablePopupOauth: true
enablePopupOauth
: true,
sessionConfig?: ({ storage?: Storage | "localStorage" | "sessionStorage" | undefined; sessionKey?: string | undefined; expirationTimeMs?: number | undefined; } & { domain?: string; }) | undefined
sessionConfig
: {
expirationTimeMs?: number | undefined
expirationTimeMs
: 1000 * 60 * 60, // 60 minutes (default is 15 min)
}, /** * External wallets (EVM + Solana) */
connectors?: CreateConnectorFn[] | undefined
connectors
:
const externalWalletsConfig: ConfigForExternalWalletsResult
externalWalletsConfig
.
ConfigForExternalWalletsResult.connectors: CreateConnectorFn[]
connectors
,
solana: { connection: Connection; adapters: WalletAdapter[] | "detect"; policyId: string | undefined; }
solana
: {
connection: Connection
connection
: new
new Connection(endpoint: string, commitmentOrConfig?: Commitment | ConnectionConfig): Connection

Establish a JSON RPC connection

Connection
(
`https://solana-devnet.g.alchemy.com/v2/${
var process: NodeJS.Process
process
.
NodeJS.Process.env: NodeJS.ProcessEnv

The process.env property returns an object containing the user environment. See environ(7).

An example of this object looks like:

jsTERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node'

It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work:

bash node -e 'process.env.foo = "bar"' && echo $foo

While the following will:


env.foo = 'bar'; console.log(env.foo); ```

Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean.

```js import env from 'node:process';

env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ```

Use `delete` to delete a property from `process.env`.

```js import env from 'node:process';

env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ```

On Windows operating systems, environment variables are case-insensitive.

```js import env from 'node:process';

env.TEST = 1; console.log(env.test); // => 1 ```

Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
env
.
string | undefined
NEXT_PUBLIC_ALCHEMY_API_KEY
}`,
),
adapters?: WalletAdapter[] | "detect" | undefined

Array of Solana wallet adapters to be used for connecting to wallets. Set to "detect" to auto-detect installed wallets, or provide explicit adapters. These adapters will be made available in the React context for wallet selection.

adapters
:
const externalWalletsConfig: ConfigForExternalWalletsResult
externalWalletsConfig
.
ConfigForExternalWalletsResult.adapters: WalletAdapter[] | "detect"
adapters
,
// optional gas sponsor for Solana
policyId?: string | undefined
policyId
:
var process: NodeJS.Process
process
.
NodeJS.Process.env: NodeJS.ProcessEnv

The process.env property returns an object containing the user environment. See environ(7).

An example of this object looks like:

jsTERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node'

It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work:

bash node -e 'process.env.foo = "bar"' && echo $foo

While the following will:


env.foo = 'bar'; console.log(env.foo); ```

Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean.

```js import env from 'node:process';

env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ```

Use `delete` to delete a property from `process.env`.

```js import env from 'node:process';

env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ```

On Windows operating systems, environment variables are case-insensitive.

```js import env from 'node:process';

env.TEST = 1; console.log(env.test); // => 1 ```

Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
env
.
string | undefined
NEXT_PUBLIC_SOLANA_POLICY_ID
,
}, }, { // Authentication ui config - your customizations here
auth?: { addPasskeyOnSignup?: boolean; header?: React.ReactNode; hideError?: boolean; onAuthSuccess?: () => void; sections: AuthType[][]; hideSignInText?: boolean; } | undefined
auth
: {
sections: AuthType[][]

Each section can contain multiple auth types which will be grouped together and separated by an OR divider

sections
: [
[{
type: "email"
type
: "email" }],
[ {
type: "passkey"
type
: "passkey" },
{
type: "social"
type
: "social",
authProviderId: KnownAuthProvider
authProviderId
: "google",
mode: "popup"
mode
: "popup" },
{
type: "social"
type
: "social",
authProviderId: KnownAuthProvider
authProviderId
: "facebook",
mode: "popup"
mode
: "popup" },
], [{
type: "external_wallets"
type
: "external_wallets", ...
const externalWalletsConfig: ConfigForExternalWalletsResult
externalWalletsConfig
.
ConfigForExternalWalletsResult.uiConfig: { walletConnect?: { projectId: string; }; wallets: string[]; chainType: ChainType[]; moreButtonText: string; hideMoreButton: boolean; numFeaturedWallets?: number; }
uiConfig
}],
],
addPasskeyOnSignup?: boolean | undefined

If this is true, then auth components will prompt users to add a passkey after signing in for the first time

addPasskeyOnSignup
: true,
showSignInText: boolean
showSignInText
: true,
}, }, );

If you don’t need customization, you can manually pass EVM connectors and Solana adapters directly into createConfig() without using the helper.

Wallet connect

If you want to access other EVM providers via WalletConnect, provide a WalletConnect Project ID in your external wallets config. You can create a WalletConnect project ID here.

export const 
const externalWalletsConfig: any
externalWalletsConfig
=
any
configForExternalWallets
({
wallets: string[]
wallets
: ["wallet_connect", "metamask", "phantom"],
chainType: string[]
chainType
: ["evm", "svm"],
walletConnectProjectId: string
walletConnectProjectId
: "your-project-id",
});

Custom connectors

If you don’t want to use pre-built UI components, you can use React hooks to customize your EOA connection.

EVM connectors

Use the useConnect hook to allow users to connect their EOA via available connectors:

import { 
const useConnect: (params?: UseConnectParameters<Config>["mutation"]) => UseConnectReturnType<Config>

Re-exported wagmi hook for connecting an EOA. This hook uses the internal wagmi config though so that the state is in sync with the rest of the Alchemy Account hook state. Useful if you wnat to connect to an EOA.

useConnect
} from "@account-kit/react";
const {
const connectors: readonly Connector<CreateConnectorFn>[] | readonly Connector<CreateConnectorFn>[]
connectors
,
const connect: ConnectMutate<Config, unknown>
connect
} =
function useConnect(params?: UseConnectParameters<Config>["mutation"]): UseConnectReturnType<Config>

Re-exported wagmi hook for connecting an EOA. This hook uses the internal wagmi config though so that the state is in sync with the rest of the Alchemy Account hook state. Useful if you wnat to connect to an EOA.

useConnect
({
onSuccess?: ((data: ConnectData<Config, Connector<CreateConnectorFn>, boolean>, variables: { chainId?: number | undefined; connector: CreateConnectorFn | Connector<CreateConnectorFn>; withCapabilities?: boolean | undefined; }, onMutateResult: unknown, context: MutationFunctionContext) => Promise<unknown> | unknown) | undefined
onSuccess
: (
data: ConnectData<Config, Connector<CreateConnectorFn>, boolean>
data
) => {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout

See util.format() for more information.

log
("Connected!",
data: ConnectData<Config, Connector<CreateConnectorFn>, boolean>
data
);
},
onError?: ((error: ConnectErrorType, variables: { chainId?: number | undefined; connector: CreateConnectorFn | Connector<CreateConnectorFn>; withCapabilities?: boolean | undefined; }, onMutateResult: unknown, context: MutationFunctionContext) => Promise<unknown> | unknown) | undefined
onError
: (
err: ConnectErrorType
err
) => {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.error(message?: any, ...optionalParams: any[]): void (+2 overloads)

Prints to stderr with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const code = 5; console.error('error #%d', code); // Prints: error #5, to stderr console.error('error', code); // Prints: error 5, to stderr

If formatting elements (e.g. %d) are not found in the first string then util.inspect() is called on each argument and the resulting string values are concatenated. See util.format() for more information.

error
("Connection failed",
err: ConnectErrorType
err
);
}, }); return ( <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
{
const connectors: readonly Connector<CreateConnectorFn>[] | readonly Connector<CreateConnectorFn>[]
connectors
.
ReadonlyArray<T>.map<JSX.Element>(callbackfn: (value: Connector<CreateConnectorFn>, index: number, array: readonly Connector<CreateConnectorFn>[]) => JSX.Element, thisArg?: any): JSX.Element[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

map
((
connector: Connector<CreateConnectorFn>
connector
) => (
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.Attributes.key?: React.Key | null | undefined
key
={
connector: Connector<CreateConnectorFn>
connector
.
id: string
id
}
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={() =>
const connect: <Connector<CreateConnectorFn>, false>(variables: ConnectVariables<Config, Connector<CreateConnectorFn>, false>, options?: { onSuccess?: ((data: ConnectData<Config, Connector<CreateConnectorFn>, false>, variables: { chainId?: number | undefined; connector: CreateConnectorFn | Connector<CreateConnectorFn>; withCapabilities?: boolean | undefined; }, onMutateResult: unknown, context: MutationFunctionContext) => void) | undefined; onError?: ((error: ConnectErrorType, variables: { chainId?: number | undefined; connector: CreateConnectorFn | Connector<CreateConnectorFn>; withCapabilities?: boolean | undefined; }, onMutateResult: unknown, context: MutationFunctionContext) => void) | undefined; onSettled?: ((data: ConnectData<...> | undefined, error: ConnectErrorType | null, variables: { chainId?: number | undefined; connector: CreateConnectorFn | Connector<CreateConnectorFn>; withCapabilities?: boolean | undefined; }, onMutateResult: unknown, context: MutationFunctionContext) => void) | undefined; } | undefined) => void
connect
({
connector: CreateConnectorFn | Connector<CreateConnectorFn>
connector
})}>
Connect with {
connector: Connector<CreateConnectorFn>
connector
.
name: string
name
}
</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
))} </
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
);

Programmatic login with a Solana adapter

Use the Solana wallet hook to select a specific adapter without showing the modal:

import { 
function useSolanaWallet(): WalletContextState

A React hook that mirrors the behavior and return type of useWallet from @solana/wallet-adapter-react, but safely degrades when Solana is not enabled for your application.

Context: This repository is an external SDK that supports multiple chains. To avoid forcing Solana dependencies on apps that do not use Solana, the Solana wallet context is only considered "active" if Solana has been initialized in the AlchemyAccountProvider configuration. If Solana is not initialized, this hook returns a stable, frozen EMPTY_WALLET_CONTEXT_STATE instead of reading from WalletContext. This prevents runtime errors when the Solana provider is not present and keeps type-safe parity with useWallet consumers.

Behavior: When Solana is configured (i.e. adapters are provided or set to "detect"), this hook returns the live WalletContext from @solana/wallet-adapter-react. Otherwise, it returns EMPTY_WALLET_CONTEXT_STATE where actions such as signMessage, sendTransaction, etc., will reject with "Solana wallet not available".

useSolanaWallet
} from "@account-kit/react";
const {
const select: (walletName: WalletName | null) => void
select
,
const wallets: Wallet[]
wallets
} =
function useSolanaWallet(): WalletContextState

A React hook that mirrors the behavior and return type of useWallet from @solana/wallet-adapter-react, but safely degrades when Solana is not enabled for your application.

Context: This repository is an external SDK that supports multiple chains. To avoid forcing Solana dependencies on apps that do not use Solana, the Solana wallet context is only considered "active" if Solana has been initialized in the AlchemyAccountProvider configuration. If Solana is not initialized, this hook returns a stable, frozen EMPTY_WALLET_CONTEXT_STATE instead of reading from WalletContext. This prevents runtime errors when the Solana provider is not present and keeps type-safe parity with useWallet consumers.

Behavior: When Solana is configured (i.e. adapters are provided or set to "detect"), this hook returns the live WalletContext from @solana/wallet-adapter-react. Otherwise, it returns EMPTY_WALLET_CONTEXT_STATE where actions such as signMessage, sendTransaction, etc., will reject with "Solana wallet not available".

useSolanaWallet
();
const
const phantom: Wallet | undefined
phantom
=
const wallets: Wallet[]
wallets
.
Array<Wallet>.find(predicate: (value: Wallet, index: number, obj: Wallet[]) => unknown, thisArg?: any): Wallet | undefined (+1 overload)

Returns the value of the first element in the array where predicate is true, and undefined otherwise.

find
((
w: Wallet
w
) =>
w: Wallet
w
.
any
name
.
any
toLowerCase
() === "phantom");
if (
const phantom: Wallet | undefined
phantom
) {
await
const select: (walletName: WalletName | null) => void
select
(
const phantom: Wallet
phantom
.
Wallet.adapter: Adapter
adapter
.
WalletAdapterProps<Name extends string = string>.name: WalletName<string>
name
);
}

Bring in an EOA as a Smart Wallet Owner

For local wallets or JSON-RPC wallets that support the EIP-1193 standard (like MetaMask, Coinbase Wallet, etc.), you can use WalletClientSigner from @aa-sdk/core to bring in these EOAs as your smart wallet owner. More info here. By making your EOA an owner of a smart account, you will have access to AA feature through your new smart wallet.

import { 
class WalletClientSigner

Represents a wallet client signer for smart accounts, providing methods to get the address, sign messages, sign typed data, and sign 7702 authorizations.

WalletClientSigner
, type
interface SmartAccountSigner<Inner = any>

A signer that can sign messages and typed data.

SmartAccountSigner
} from "@aa-sdk/core";
import {
function createWalletClient<transport extends Transport, chain extends Chain | undefined = undefined, accountOrAddress extends Account | Address | undefined = undefined, rpcSchema extends RpcSchema | undefined = undefined>(parameters: WalletClientConfig<transport, chain, accountOrAddress, rpcSchema>): WalletClient<transport, chain, ParseAccount<accountOrAddress>, rpcSchema>

Creates a Wallet Client with a given Transport configured for a Chain.

Docs: https://viem.sh/docs/clients/wallet

A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.

The Wallet Client supports signing over: JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc). Local Accounts (e.g. private key/mnemonic wallets).

createWalletClient
,
function custom<provider extends EthereumProvider>(provider: provider, config?: CustomTransportConfig): CustomTransport
custom
} from "viem";
import {
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 751532; }; readonly ensUniversalResolver: { readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee"; readonly blockCreated: 8928790; }; }; ... 14 more ...; verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined; }
sepolia
} from "viem/chains";
import {
function createModularAccountV2<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>>(config: CreateModularAccountV2Params<TTransport, TSigner>): Promise<ModularAccountV2<TSigner>> (+1 overload)
createModularAccountV2
} from "@account-kit/smart-contracts";
const
const externalProvider: any
externalProvider
=
var window: Window & typeof globalThis
window
.
any
ethereum
; // or another EIP-1193 provider
const
const walletClient: { account: undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; cacheTime: number; ccipRead?: false | { request?: (parameters: CcipRequestParameters) => Promise<CcipRequestReturnType>; } | undefined; chain: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 16 more ...; verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined; }; ... 36 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
walletClient
=
createWalletClient<CustomTransport, { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 751532; }; readonly ensUniversalResolver: { readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee"; readonly blockCreated: 8928790; }; }; ... 14 more ...; verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined; }, undefined, undefined>(parameters: { ...; }): { ...; }

Creates a Wallet Client with a given Transport configured for a Chain.

Docs: https://viem.sh/docs/clients/wallet

A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.

The Wallet Client supports signing over: JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc). Local Accounts (e.g. private key/mnemonic wallets).

createWalletClient
({
chain?: Chain | { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 751532; }; readonly ensUniversalResolver: { readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee"; readonly blockCreated: 8928790; }; }; ... 14 more ...; verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined; } | undefined

Chain for the client.

chain
:
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 751532; }; readonly ensUniversalResolver: { readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee"; readonly blockCreated: 8928790; }; }; ... 14 more ...; verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined; }
sepolia
,
transport: CustomTransport

The RPC transport

transport
:
custom<any>(provider: any, config?: CustomTransportConfig): CustomTransport
custom
(
const externalProvider: any
externalProvider
),
}); export const
const signer: SmartAccountSigner<any>
signer
:
interface SmartAccountSigner<Inner = any>

A signer that can sign messages and typed data.

SmartAccountSigner
= new
new WalletClientSigner(client: WalletClient, signerType: string): WalletClientSigner

Initializes a signer with a given wallet client and signer type.

WalletClientSigner
(
const walletClient: { account: undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; cacheTime: number; ccipRead?: false | { request?: (parameters: CcipRequestParameters) => Promise<CcipRequestReturnType>; } | undefined; chain: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 16 more ...; verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined; }; ... 36 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
walletClient
,
"json-rpc", ); // Connect your signer to your smart account const
const account: ModularAccountV2<SmartAccountSigner<any>>
account
= await
createModularAccountV2<any, SmartAccountSigner<any>>(config: CreateModularAccountV2Params<any, SmartAccountSigner<any>>): Promise<ModularAccountV2<SmartAccountSigner<any>>> (+1 overload)
createModularAccountV2
({
chain: Chain
chain
:
const sepolia: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 751532; }; readonly ensUniversalResolver: { readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee"; readonly blockCreated: 8928790; }; }; ... 14 more ...; verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined; }
sepolia
,
transport: any
transport
:
any
alchemyTransport
,
signer: SmartAccountSigner<any>
signer
:
const signer: SmartAccountSigner<any>
signer
, // your EOA that you've brought in as an owner
});