Pay with Native

This page details the process of paying the gas of user operations (UserOps) using native tokens.

This method doesn’t require a paymaster, making it a straightforward and direct way to send UserOps while paying with native tokens from your smart account.

Example Code

1. Create an account using createKernelAccount from @zerodev/sdk

const entryPoint = getEntryPoint("0.7");
const kernelVersion = KERNEL_V3_1;

const ecdsaValidator = await signerToEcdsaValidator(publicClient, {
    signer,
    entryPoint,
    kernelVersion,
});

const account = await createKernelAccount(publicClient, {
    plugins: {
      sudo: ecdsaValidator,
    },
    entryPoint,
    kernelVersion
});

2. Create an Client using createKernelAccountClient adding Gelato Bundler RPC

When using the Gelato Bundler, it's essential to use a specialized gas estimation method when creating the kernelClient.

const gasPrices = await bundlerClient.request<EthGetUserOperationGasPriceRpc>({
            method: "eth_getUserOperationGasPrice",
            params: [],
});
const maxFeePerGas = BigInt(gasPrices.maxFeePerGas),
const maxPriorityFeePerGas = BigInt(gasPrices.maxPriorityFeePerGas),
const kernelClient = createKernelAccountClient({
    account,
    chain: baseSepolia,
    bundlerTransport: http(STAGING_BUNDLER_RPC),
    client: publicClient,
    userOperation: {
      estimateFeesPerGas: async ({ bundlerClient }) => {
        const gasPrices =
          await bundlerClient.request<EthGetUserOperationGasPriceRpc>({
            method: "eth_getUserOperationGasPrice",
            params: [],
          });
        return {
          maxFeePerGas: BigInt(gasPrices.maxFeePerGas),
          maxPriorityFeePerGas: BigInt(gasPrices.maxPriorityFeePerGas),
        };
      },
    },
});

Gas Config Types

type GasPrices = {
  maxFeePerGas: string;
  maxPriorityFeePerGas: string;
};

type EthGetUserOperationGasPriceRpc = {
  ReturnType: GasPrices;
  Parameters: [];
};

3. Send UserOperation using kernelClient

console.log("Preparing dummy transaction");
const callData = await kernelClient.account.encodeCalls([
    {
      to: "0x0000000000000000000000000000000000000000",
      value: BigInt(0),
      data: "0x",
    },
  ]);

console.log("Sending user operation...");
const userOpHash = await kernelClient.sendUserOperation({ callData });
console.log("UserOp taskId:", userOpHash);

4. Check UserOp status using Relay API

The userOpHash, when using the Gelato Bundler, is equivalent to the Task ID. This Task ID can be easily debugged through the Relay API, providing a streamlined method for troubleshooting and monitoring the status of transactions. Checkout here.

Explore the complete example code that demonstrates how to pay using native tokens while using Gelato Bundler with the ZeroDev Kernel here, as well as how to integrate with a Safe account here.

Last updated