3rd Party Bundlers

The SmartAccountClient within @aa-sdk/core is unopinionated about which bundler you use, so you can connect to any RPC provider really simply.

Usage

If we look at the example for creating a SmartAccountClient:

import { 
function createSmartAccountClient<TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined, TContext extends UserOperationContext | undefined = UserOperationContext | undefined>(config: SmartAccountClientConfig<TTransport, TChain, TAccount, TContext>): SmartAccountClient<TTransport, TChain, TAccount>
createSmartAccountClient
} from "@aa-sdk/core";
import {
function http<rpcSchema extends RpcSchema | undefined = undefined, raw extends boolean = false>(url?: string | undefined, config?: HttpTransportConfig<rpcSchema, raw>): HttpTransport<rpcSchema, raw>
http
} 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"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
} from "viem/chains";
const
const client: { [x: string]: unknown; account: SmartContractAccount | undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 83 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createSmartAccountClient<HttpTransport<undefined, false>, { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }, SmartContractAccount | undefined, UserOperationContext | undefined>(config: { ...; }): { ...; }
createSmartAccountClient
({
transport: HttpTransport<undefined, false>

The RPC transport

transport
:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http
("https://polygon-mumbai.g.alchemy.com/v2/demo"),
chain?: Chain | { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://sepolia.etherscan.io"; readonly apiUrl: "https://api-sepolia.etherscan.io/api"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | 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"; }; }; ... 11 more ...; serializers?: ChainSerializers<...> | undefined; }
sepolia
,
});

You can see that we set the transport to http("https://polygon-mumbai.g.alchemy.com/v2/demo"). You can swap out that the url in the http function to any other provider’s URL.

Depending on your provider, you may have to pass in custom logic for the gasEstimator and feeEstimator properties when calling createSmartAccountClient. Consult with your provider on what the correct logic is.

Splitting Bundler traffic and Node RPC traffic

It might be the case that you want to use a different RPC provider for your bundler traffic and your node traffic. This is a common use case, and you can do this by leveraging the split transport and passing it to your createSmartAccountClient call. For example:

Using Alchemy Bundler and Gas Manager with 3rd Party Node RPCs

If you want to split your node traffic from Alchemy’s Bundler traffic, you can do this with the alchemyTransport

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
} from "@account-kit/infra";
const
const alchemyTransport: AlchemyTransport
alchemyTransport
=
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
({
alchemyConnection: ({ apiKey: string; rpcUrl?: undefined; jwt?: undefined; } | { jwt: string; rpcUrl?: undefined; apiKey?: undefined; } | { rpcUrl: string; apiKey?: undefined; jwt?: undefined; } | { rpcUrl: string; jwt: string; apiKey?: undefined; }) & { ...; }
alchemyConnection
: {
apiKey: string
apiKey
: "your-api-key" },
nodeRpcUrl: string
nodeRpcUrl
: "YOUR_NODE_RPC_URL",
}); // now use this transport in a client

Using two different 3rd Party Bundler and Node RPCs

import { 
const split: (params: SplitTransportParams) => CustomTransport

The Split Transport allows you to split RPC traffic for specific methods across different RPC providers. This is done by specifying the methods you want handled specially as overrides and providing a fallback transport for all other methods.

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

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

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

A Public Client is an interface to "public" JSON-RPC API methods such as retrieving block numbers, transactions, reading from smart contracts, etc through Public Actions.

createPublicClient
,
function http<rpcSchema extends RpcSchema | undefined = undefined, raw extends boolean = false>(url?: string | undefined, config?: HttpTransportConfig<rpcSchema, raw>): HttpTransport<rpcSchema, raw>
http
} from "viem";
const
const bundlerMethods: string[]
bundlerMethods
= [
"eth_sendUserOperation", "eth_estimateUserOperationGas", "eth_getUserOperationReceipt", "eth_getUserOperationByHash", "eth_supportedEntryPoints", ]; const
const clientWithSplit: { account: undefined; batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined; ... 64 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
clientWithSplit
=
createPublicClient<CustomTransport, undefined, undefined, undefined>(parameters: { batch?: { multicall?: boolean | Prettify<MulticallBatchOptions> | undefined; } | undefined | undefined; ... 7 more ...; transport: CustomTransport; }): { ...; }

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

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

A Public Client is an interface to "public" JSON-RPC API methods such as retrieving block numbers, transactions, reading from smart contracts, etc through Public Actions.

createPublicClient
({
transport: CustomTransport

The RPC transport

transport
:
function split(params: SplitTransportParams): CustomTransport

The Split Transport allows you to split RPC traffic for specific methods across different RPC providers. This is done by specifying the methods you want handled specially as overrides and providing a fallback transport for all other methods.

split
({
SplitTransportParams.overrides: { methods: string[]; transport: Transport; }[]
overrides
: [
{
methods: string[]
methods
:
const bundlerMethods: string[]
bundlerMethods
,
transport: Transport
transport
:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http
("BUNDLER_RPC_URL"),
}, ],
SplitTransportParams.fallback: Transport
fallback
:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http
("OTHER_RPC_URL"),
}), });

Zora and Fraxtal

Using a split Bundler and Node RPC setup is required for Fraxtal, Fraxtal Testnet, Zora, and Zora Sepolia networks since Alchemy currently only supports Account Abstraction endpoints for those networks. Please refer to documentation from Frax and Zora about RPC options.

See this guide for more information on using AA only chains with Account Kit.