How to sponsor gas for a User Operation
Gas fees are a significant barrier to entry for new user of your app. With Smart Wallets you can remove this barrier by sponsoring gas fees for transactions via the Gas Manager. This guide explains how to sponsor gas by creating a gas policy, linking it to your client, and sending sponsored UserOperations
(UOs) from a smart account.
After setting up Smart Wallets in your project, follow these steps to sponsor gas.
Create a Gas Manager 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.
Set the Policy ID globally
When creating your Account Kit config, you can optionally pass in a Gas Policy ID. This will enable all UOs sent by the useSendUserOperation
hook to be sponsored by the policy you created.
Copy it and then replace the GAS_MANAGER_POLICY_ID
in the snippet below.
Remember to replace ALCHEMY_API_KEY
with your Alchemy API key. If you don’t
have one yet, you can create an API key on the Alchemy
dashboard.
import { const createConfig: (params: BaseCreateConfigProps) => AlchemyAccountsConfigCreates an AlchemyAccountsConfig by using the provided parameters to configure the core settings, including the required transport. It includes a signer creation function internally.
createConfig } from "@account-kit/react-native";
import { const sepolia: Chainsepolia } from "@account-kit/infra";
export const const config: AlchemyAccountsConfigconfig = function createConfig(params: BaseCreateConfigProps): AlchemyAccountsConfigCreates an AlchemyAccountsConfig by using the provided parameters to configure the core settings, including the required transport. It includes a signer creation function internally.
createConfig({
apiKey: stringapiKey: "ALCHEMY_API_KEY",
chain: Chainchain: const sepolia: Chainsepolia,
policyId?: string | string[] | undefinedpolicyId: "GAS_MANAGER_POLICY_ID",
});
Now you can follow the guide for Sending user operations to send sponsored UOs from your smart account!
Set the gas policy ID per UserOperation
If you want more control over which UOs are sponsored, then you can set the policy ID on a specific instance of the Smart Account Client
returned by the useSmartAccountClient()
hook.
import React from "react";
import { class ViewView, const Pressable: React.ForwardRefExoticComponent<PressableProps & React.RefAttributes<View>>Pressable, class TextText } from "react-native";
import {
function useSmartAccountClient<TChain extends Chain | undefined = Chain | undefined, TAccount extends SupportedAccountTypes | undefined = "ModularAccountV2">(args: UseSmartAccountClientProps<TChain, TAccount>): UseSmartAccountClientResult<TChain, SupportedAccount<TAccount extends undefined ? "ModularAccountV2" : TAccount>>useSmartAccountClient,
function useSendUserOperation<TEntryPointVersion extends GetEntryPointFromAccount<TAccount>, TAccount extends SupportedAccounts = SupportedAccounts>(params: UseSendUserOperationArgs<TEntryPointVersion, TAccount>): UseSendUserOperationResult<TEntryPointVersion, TAccount>A hook that returns functions for sending user operations. You can also optionally wait for a user operation to be mined and get the transaction hash before returning using waitForTx
. Like any method that takes a smart account client, throws an error if client undefined or is signer not authenticated.
useSendUserOperation,
} from "@account-kit/react-native";
export default function function MyComponent(): JSX.ElementMyComponent() {
const { const client: {
account: ModularAccountV2<AlchemySigner>;
batch?: {
multicall?: boolean | Prettify<MulticallBatchOptions> | undefined;
} | undefined;
... 84 more ...;
extend: <const client extends {
...;
} & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>;
} | undefinedclient } = useSmartAccountClient<Chain | undefined, "ModularAccountV2">(args: UseSmartAccountClientProps<Chain | undefined, "ModularAccountV2">): UseSmartAccountClientResult<Chain | undefined, ModularAccountV2<...>>useSmartAccountClient({
policyId?: string | string[] | undefinedpolicyId: "GAS_MANAGER_POLICY_ID",
});
const { const sendUserOperation: UseMutateFunction<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<SupportedAccounts>, unknown>sendUserOperation } = useSendUserOperation<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>(params: UseSendUserOperationArgs<keyof EntryPointRegistryBase<unknown>, SupportedAccounts>): UseSendUserOperationResult<...>A hook that returns functions for sending user operations. You can also optionally wait for a user operation to be mined and get the transaction hash before returning using waitForTx
. Like any method that takes a smart account client, throws an error if client undefined or is signer not authenticated.
useSendUserOperation({ client: {
account: SupportedAccounts;
batch?: {
multicall?: boolean | Prettify<MulticallBatchOptions> | undefined;
} | undefined;
... 84 more ...;
extend: <const client extends {
...;
} & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>;
} | {
...;
} | {
...;
} | {
...;
} | undefinedclient });
return (
<class ViewView>
<const Pressable: React.ForwardRefExoticComponent<PressableProps & React.RefAttributes<View>>Pressable
PressableProps.onPress?: ((event: GestureResponderEvent) => void) | null | undefinedCalled when a single tap gesture is detected.
onPress={() =>
const sendUserOperation: (variables: SendUserOperationParameters<SupportedAccounts>, options?: MutateOptions<SendUserOperationWithEOA<keyof EntryPointRegistryBase<unknown>>, Error, SendUserOperationParameters<...>, unknown> | undefined) => voidsendUserOperation({
uo: UserOperationCallData | BatchUserOperationCallDatauo: {
target: `0x${string}`target: "0xTARGET_ADDRESS",
data: `0x${string}`data: "0x",
value?: bigint | undefinedvalue: 0n,
},
})
}
>
<class ViewView>
<class TextText>Send Sponsored User Operation</class TextText>
</class ViewView>
</const Pressable: React.ForwardRefExoticComponent<PressableProps & React.RefAttributes<View>>Pressable>
</class ViewView>
);
}