Sponsor Gas

In the Quickstart, we covered an example that sends a user operation with gas sponsorship using our Gas Manager. In this guide, we’ll recap how to sponsor gas using the Gas Manager, and also show you how to use other paymaster providers as well.

1. Create gas policy

A gas manager policy is a set of rules that define which UOs are eligible for gas sponsorship. You can control which operations are eligible for sponsorship by defining rules:

  • Spending rules: limit the amount of money or the number of user ops that can be sponsored by this policy
  • Allowlist: restrict wallet addresses that are eligible for sponsorship. The policy will only sponsor gas for UOs that were sent by addresses on this list.
  • Blocklist: ban certain addresses from receiving sponsorship under this policy
  • Policy duration: define the duration of your policy and the sponsorship expiry period. This is the period for which the Gas Manager signature (paymaster data) will remain valid once it is generated.

To learn more about policy configuration, refer to the guide on setting up a gas manager policy.

Once you have decided on policy rules for your app, create a policy in the Gas Manager dashboard.

Now you should have a Gas policy created with a policy id you can use to sponsor gas for your users.

Policy ID

2. Create a Smart Account Client

Now you can create a Smart Account Client which is configured to sponsor gas.

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
,
function createAlchemySmartAccountClient<TChain extends Chain = Chain, TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined, TContext extends UserOperationContext | undefined = UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<TChain, TAccount, TContext>): AlchemySmartAccountClient<TChain, TAccount, Record<string, never>, TContext>
createAlchemySmartAccountClient
,
const sepolia: Chain
sepolia
,
} from "@account-kit/infra"; import {
function createLightAccount<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>, TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0">(config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>): Promise<LightAccount<TSigner, TLightAccountVersion>>
createLightAccount
} from "@account-kit/smart-contracts";
// You can replace this with any signer you'd like // We're using a LocalAccountSigner to generate a local key to sign with import {
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} from "@aa-sdk/core";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
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
({
apiKey: string
apiKey
: "YOUR_API_KEY",
}); export const
const client: { [x: string]: never; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 85 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createAlchemySmartAccountClient<Chain, LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">, UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<...>): { ...; }
createAlchemySmartAccountClient
({
transport: AlchemyTransport

The RPC transport

transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
policyId?: string | string[] | undefined
policyId
: "YOUR_POLICY_ID",
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
account?: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0"> | undefined
account
: await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
}), });

3. Send a user operation with gas sponsorship

import { 
import client
client
} from "./client";
const {
const hash: any
hash
} = await
import client
client
.
any
sendUserOperation
({
uo: { target: string; data: string; value: bigint; }
uo
: {
target: string
target
: "0xTARGET_ADDRESS",
data: string
data
: "0x",
value: bigint
value
: 0n,
}, });
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
,
function createAlchemySmartAccountClient<TChain extends Chain = Chain, TAccount extends SmartContractAccount | undefined = SmartContractAccount | undefined, TContext extends UserOperationContext | undefined = UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<TChain, TAccount, TContext>): AlchemySmartAccountClient<TChain, TAccount, Record<string, never>, TContext>
createAlchemySmartAccountClient
,
const sepolia: Chain
sepolia
,
} from "@account-kit/infra"; import {
function createLightAccount<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>, TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0">(config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>): Promise<LightAccount<TSigner, TLightAccountVersion>>
createLightAccount
} from "@account-kit/smart-contracts";
// You can replace this with any signer you'd like // We're using a LocalAccountSigner to generate a local key to sign with import {
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} from "@aa-sdk/core";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
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
({
apiKey: string
apiKey
: "YOUR_API_KEY",
}); export const
const client: { [x: string]: never; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 85 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createAlchemySmartAccountClient<Chain, LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">, UserOperationContext | undefined>(params: AlchemySmartAccountClientConfig<...>): { ...; }
createAlchemySmartAccountClient
({
transport: AlchemyTransport

The RPC transport

transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
policyId?: string | string[] | undefined
policyId
: "YOUR_POLICY_ID",
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
account?: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0"> | undefined
account
: await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
}), });

If you’re using a 3rd party paymaster to sponsor gas, but our Bundler RPC endpoints, the set up is a bit more involved, but easily doable.

Prerequisite: install aa-sdk/core

Because the Smart Account Client exported from @account-kit/infra assumes you’re using our infra for both gas sponsorship and bundler RPCs, you’ll need to install @aa-sdk/core so you can create more configurable clients.

$yarn add @aa-sdk/core

ERC-7677 compliant paymasters

If your paymaster is ERC-7677 compliant, we export a paymaster middleware that makes it really easy to integrate with that paymaster.

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
,
function erc7677Middleware<TContext extends Record<string, any> | undefined = Record<string, any> | undefined>(params?: Erc7677MiddlewareParams<TContext>): Required<Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData">>

Middleware function for interacting with ERC-7677 enabled clients. It supports resolving paymaster and data fields for user operations. This middleware assumes that your RPC provider supports the ERC-7677 methods (pm_getPaymasterStubData and pm_getPaymasterData).

erc7677Middleware
,
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 {
const sepolia: Chain
sepolia
,
const alchemyFeeEstimator: (transport: AlchemyTransport) => ClientMiddlewareFn

Function that estimates the transaction fees using Alchemy methods for a given client. It fetches the latest block and estimates the max priority fee per gas, applying any overrides or fee options provided.

alchemyFeeEstimator
,
const createAlchemyPublicRpcClient: ({ transport, chain, }: { transport: AlchemyTransport; chain: Chain | undefined; }) => ClientWithAlchemyMethods

Creates an Alchemy public RPC client with the provided chain, connection configuration, and optional fetch options. The client has alchemy methods and can dynamically update HTTP headers.

createAlchemyPublicRpcClient
,
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"; import {
function createLightAccount<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>, TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0">(config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>): Promise<LightAccount<TSigner, TLightAccountVersion>>
createLightAccount
} from "@account-kit/smart-contracts";
// You can replace this with any signer you'd like // We're using a LocalAccountSigner to generate a local key to sign with import {
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} 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
,
function custom<provider extends EthereumProvider>(provider: provider, config?: CustomTransportConfig): CustomTransport
custom
} from "viem";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
// 1. create an alchemy rpc client 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
({
apiKey: string
apiKey
: "API_KEY",
}); const
const alchemyRpcClient: ClientWithAlchemyMethods
alchemyRpcClient
=
function createAlchemyPublicRpcClient({ transport, chain, }: { transport: AlchemyTransport; chain: Chain | undefined; }): ClientWithAlchemyMethods

Creates an Alchemy public RPC client with the provided chain, connection configuration, and optional fetch options. The client has alchemy methods and can dynamically update HTTP headers.

createAlchemyPublicRpcClient
({
chain: Chain | undefined
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
}); // 2. create a split transport to route traffic between the paymaster and the bundler const
const transport: CustomTransport
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
: ["pm_getPaymasterStubData", "pm_getPaymasterData"],
transport: Transport
transport
:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http
("PAYMASTER_URL"),
}, ],
SplitTransportParams.fallback: Transport
fallback
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
}); // 3. create smart account client export const
const client: { [x: string]: unknown; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createSmartAccountClient<CustomTransport, Chain, LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">, UserOperationContext>(config: { ...; }): { ...; }
createSmartAccountClient
({
transport: CustomTransport

The RPC transport

transport
,
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
account?: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0"> | undefined
account
: await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
}), // this is required to get correct fee estimates when using our Bundler RPC
feeEstimator?: ClientMiddlewareFn<UserOperationContext> | undefined
feeEstimator
:
function alchemyFeeEstimator(transport: AlchemyTransport): ClientMiddlewareFn

Function that estimates the transaction fees using Alchemy methods for a given client. It fetches the latest block and estimates the max priority fee per gas, applying any overrides or fee options provided.

alchemyFeeEstimator
(
const alchemyRpcClient: ClientWithAlchemyMethods
alchemyRpcClient
),
...
erc7677Middleware<Record<string, any> | undefined>(params?: Erc7677MiddlewareParams<Record<string, any> | undefined, keyof EntryPointRegistryBase<unknown>> | undefined): Required<Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData">>

Middleware function for interacting with ERC-7677 enabled clients. It supports resolving paymaster and data fields for user operations. This middleware assumes that your RPC provider supports the ERC-7677 methods (pm_getPaymasterStubData and pm_getPaymasterData).

erc7677Middleware
(),
});

Other 3rd Party Paymasters

If your paymaster provider is not ERC-7677 compliant, the setup is very similar, but you’ll need to provide your own paymasterAndData and dummyPaymasterAndData middleware overrides to handle the paymaster calls. In this example, we’ll show you how to use Stackup to provide ERC-20 gas payments.

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
,
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
,
function deepHexlify(obj: any): any

Recursively converts all values in an object to hex strings

deepHexlify
,
function resolveProperties<T>(object: Deferrable<T>): Promise<T>

Await all of the properties of a Deferrable object

resolveProperties
,
} from "@aa-sdk/core"; import {
const sepolia: Chain
sepolia
,
const alchemyFeeEstimator: (transport: AlchemyTransport) => ClientMiddlewareFn

Function that estimates the transaction fees using Alchemy methods for a given client. It fetches the latest block and estimates the max priority fee per gas, applying any overrides or fee options provided.

alchemyFeeEstimator
,
const createAlchemyPublicRpcClient: ({ transport, chain, }: { transport: AlchemyTransport; chain: Chain | undefined; }) => ClientWithAlchemyMethods

Creates an Alchemy public RPC client with the provided chain, connection configuration, and optional fetch options. The client has alchemy methods and can dynamically update HTTP headers.

createAlchemyPublicRpcClient
,
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"; import {
function createLightAccount<TTransport extends Transport = Transport, TSigner extends SmartAccountSigner = SmartAccountSigner<any>, TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0">(config: CreateLightAccountParams<TTransport, TSigner, TLightAccountVersion>): Promise<LightAccount<TSigner, TLightAccountVersion>>
createLightAccount
} from "@account-kit/smart-contracts";
// You can replace this with any signer you'd like // We're using a LocalAccountSigner to generate a local key to sign with import {
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
} 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
,
function custom<provider extends EthereumProvider>(provider: provider, config?: CustomTransportConfig): CustomTransport
custom
} from "viem";
import {
function generatePrivateKey(): Hex
generatePrivateKey
} from "viem/accounts";
// 1. create an alchemy rpc client 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
({
apiKey: string
apiKey
: "API_KEY",
}); const
const alchemyRpcClient: ClientWithAlchemyMethods
alchemyRpcClient
=
function createAlchemyPublicRpcClient({ transport, chain, }: { transport: AlchemyTransport; chain: Chain | undefined; }): ClientWithAlchemyMethods

Creates an Alchemy public RPC client with the provided chain, connection configuration, and optional fetch options. The client has alchemy methods and can dynamically update HTTP headers.

createAlchemyPublicRpcClient
({
chain: Chain | undefined
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
}); // 2. create a split transport to route traffic between the paymaster and the bundler const
const transport: CustomTransport
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
: ["pm_sponsorUserOperation"],
// TODO: Replace with your stackup API key here (https://docs.stackup.sh/docs/paymaster-api)
transport: Transport
transport
:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http
("https://api.stackup.sh/v1/paymaster/STACKUP_API_KEY"),
}, ],
SplitTransportParams.fallback: Transport
fallback
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
}); // 3. create smart account client export const
const client: { [x: string]: unknown; account: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">; ... 84 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; }
client
=
createSmartAccountClient<CustomTransport, Chain, LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">, UserOperationContext>(config: { ...; }): { ...; }
createSmartAccountClient
({
transport: CustomTransport

The RPC transport

transport
,
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
account?: LightAccount<LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0"> | undefined
account
: await
createLightAccount<AlchemyTransport, LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>, "v2.0.0">(config: CreateLightAccountParams<...>): Promise<...>
createLightAccount
({
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
const alchemyTransport: AlchemyTransport
alchemyTransport
,
signer: LocalAccountSigner<{ address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; ... 6 more ...; type: "local"; }>
signer
:
class LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>

Represents a local account signer and provides methods to sign messages and transactions, as well as static methods to create the signer from mnemonic or private key.

LocalAccountSigner
.
LocalAccountSigner<T extends HDAccount | PrivateKeyAccount | LocalAccount>.privateKeyToAccountSigner(key: Hex): LocalAccountSigner<PrivateKeyAccount>

Creates a LocalAccountSigner instance using the provided private key.

privateKeyToAccountSigner
(
function generatePrivateKey(): Hex
generatePrivateKey
()),
}), // Bypasses alchemy gas estimation and instead uses Stackup for gas estimation // If your paymaster doesn't provide gas estimation, then don't replace the gasEstimator
gasEstimator?: ClientMiddlewareFn<UserOperationContext> | undefined
gasEstimator
: async (
userOp: Deferrable<UserOperationStruct<TEntryPointVersion>>
userOp
) => ({
...
userOp: Deferrable<UserOperationStruct<TEntryPointVersion>>
userOp
,
callGasLimit?: PromiseOrValue<UserOperationStruct<TEntryPointVersion>["callGasLimit"]> | undefined
callGasLimit
: "0x0",
preVerificationGas?: PromiseOrValue<UserOperationStruct<TEntryPointVersion>["preVerificationGas"]> | undefined
preVerificationGas
: "0x0",
verificationGasLimit?: PromiseOrValue<UserOperationStruct<TEntryPointVersion>["verificationGasLimit"]> | undefined
verificationGasLimit
: "0x0",
}), // this is required to get correct fee estimates when using our Bundler RPC
feeEstimator?: ClientMiddlewareFn<UserOperationContext> | undefined
feeEstimator
:
function alchemyFeeEstimator(transport: AlchemyTransport): ClientMiddlewareFn

Function that estimates the transaction fees using Alchemy methods for a given client. It fetches the latest block and estimates the max priority fee per gas, applying any overrides or fee options provided.

alchemyFeeEstimator
(
const alchemyRpcClient: ClientWithAlchemyMethods
alchemyRpcClient
),
dummyPaymasterAndData?: ClientMiddlewareFn<UserOperationContext> | undefined
dummyPaymasterAndData
: async (
userop: Deferrable<UserOperationStruct<TEntryPointVersion>>
userop
) => ({
...
userop: Deferrable<UserOperationStruct<TEntryPointVersion>>
userop
,
paymasterAndData: string
paymasterAndData
: "0x",
}),
paymasterAndData?: ClientMiddlewareFn<UserOperationContext> | undefined
paymasterAndData
: async (
userop: Deferrable<UserOperationStruct<TEntryPointVersion>>
userop
, {
client: C extends MiddlewareClient
client
,
account: TAccount extends SmartContractAccount
account
}) => {
const
const pmResponse: any
pmResponse
: any = await
client: C extends MiddlewareClient
client
.
request: <undefined, { method: "eth_sendUserOperation"; params: [UserOperationRequest, `0x${string}`]; } | { method: "eth_estimateUserOperationGas"; params: [UserOperationRequest, `0x${string}`, (RpcStateOverride | undefined)?]; } | ... 44 more ... | { ...; }, string | ... 14 more ... | null>(args: { method: "eth_sendUserOperation"; params: [UserOperationRequest, `0x${string}`]; } | ... 45 more ... | { ...; }, options?: EIP1193RequestOptions | undefined) => Promise<...>

Request function wrapped with friendly error handling

request
({
// @ts-ignore
method: "pm_sponsorUserOperation"
method
: "pm_sponsorUserOperation",
params: [any, `0x${string}`, { type: string; }]
params
: [
function deepHexlify(obj: any): any

Recursively converts all values in an object to hex strings

deepHexlify
(await
resolveProperties<UserOperationStruct<TEntryPointVersion>>(object: Deferrable<UserOperationStruct<TEntryPointVersion>>): Promise<...>

Await all of the properties of a Deferrable object

resolveProperties
(
userop: Deferrable<UserOperationStruct<TEntryPointVersion>>
userop
)),
account: TAccount extends SmartContractAccount
account
.
getEntryPoint: () => EntryPointDef<keyof EntryPointRegistryBase<unknown>>
getEntryPoint
().
address: `0x${string}`
address
,
{ // @ts-ignore
type: string
type
: "payg", // Replace with ERC20 context based on stackups documentation
}, ], }); return { ...
userop: Deferrable<UserOperationStruct<TEntryPointVersion>>
userop
,
...
const pmResponse: any
pmResponse
,
}; }, });