One of the biggest UX enhancements unlocked by Base Account is the ability for app developers to sponsor their users’ transactions. If your app supports Base Account, you can start sponsoring your users’ transactions by using standardized Paymaster service communication enabled by new wallet RPC methods.

This guide is specific to using Base Account, you can find our more about using Paymasters with Base in the Base Go Gasless page.

Implementation Guide

1

Set up your Paymaster service

As a prerequisite, you’ll need to obtain a Paymaster service URL from a Paymaster service provider.

We’ll use Coinbase Developer Platform as a Paymaster service provider, currently offering up to $15k in gas credits as part of the Base Gasless Campaign.

ERC-7677-Compliant Paymaster Providers

If you choose to use a different Paymaster service provider, ensure they are ERC-7677-compliant.

Once you have signed up for Coinbase Developer Platform, you get your Paymaster service URL by navigating to Onchain Tools > Paymaster as shown below:

Paymaster CDP

Should you create a proxy for your Paymaster service?

We recommend using a proxy to protect the Paymaster service URL to prevent it from being exposed/leaked on a frontend client.

For local development, you can use the same URL for the Paymaster service and the proxy.

Once you have your Paymaster service URL, you can proceed to setting up your contracts allowlist. This is a list of contracts and function calls that you want to be sponsored by the Paymaster.

Paymaster CDP Allowlist

Congrats! You’ve set up your Paymaster service and contracts allowlist. It’s time to set up the Base Account SDK.

You can also choose to create custom advanced policies !

You can create a willSponsor function to add some extra validation if you need more control over the policy enforcement.

willSponsor is most likely not needed if you are using Coinbase Developer Platform as it has built-in policy enforcement features, but know that this is still possible if you need it.

2

Setup Base Account SDK

Install and initialize the Base Account SDK to interact with Base Account:

Installation

npm install @base-org/account

Initialize the SDK

import { createBaseAccountSDK, base } from '@base-org/account';

const sdk = createBaseAccountSDK({
  appName: 'Paymaster Demo',
  appLogoUrl: 'https://base.org/logo.png',
  appChainIds: [base.constants.CHAIN_IDS.baseSepolia], // or base.constants.CHAIN_IDS.base for mainnet
});

const provider = sdk.getProvider();
3

Send transactions with Paymaster service capability

Once you have your Paymaster service set up, you can now use wallet_sendCalls with paymaster capabilities to sponsor transactions.

Pass in the proxy URL

If you set up a proxy in your app’s backend as recommended in step (1) above, you’ll want to pass in the proxy URL you created.

Basic Sponsored Transaction

Here’s how to send a sponsored transaction using the wallet_sendCalls RPC method:

import { createBaseAccountSDK, getCryptoKeyAccount, base } from '@base-org/account';
import { numberToHex, encodeFunctionData, parseEther } from 'viem';

// Example NFT contract ABI
const nftABI = [
  {
    name: 'safeMint',
    type: 'function',
    stateMutability: 'nonpayable',
    inputs: [{ name: 'to', type: 'address' }],
    outputs: []
  }
] as const;

async function sendSponsoredTransaction() {
  const sdk = createBaseAccountSDK({
    appName: 'Paymaster Demo',
    appLogoUrl: 'https://base.org/logo.png',
    appChainIds: [base.constants.CHAIN_IDS.baseSepolia],
  });

  const provider = sdk.getProvider();
  
  try {
    // Get the user's account
    const cryptoAccount = await getCryptoKeyAccount();
    const fromAddress = cryptoAccount?.account?.address;
    
    if (!fromAddress) {
      throw new Error('No account found');
    }

    // Your Paymaster service URL (use your proxy URL)
    const paymasterServiceUrl = process.env.NEXT_PUBLIC_PAYMASTER_PROXY_SERVER_URL;
    
    // Prepare the transaction call
    const nftAddress = '0x119Ea671030FBf79AB93b436D2E20af6ea469a19';
    const calls = [
      {
        to: nftAddress,
        value: '0x0',
        data: encodeFunctionData({
          abi: nftABI,
          functionName: 'safeMint',
          args: [fromAddress]
        })
      }
    ];

    // Send the transaction with paymaster capabilities
    const result = await provider.request({
      method: 'wallet_sendCalls',
      params: [{
        version: '1.0',
        chainId: numberToHex(base.constants.CHAIN_IDS.baseSepolia),
        from: fromAddress,
        calls: calls,
        capabilities: {
          paymasterService: {
            url: paymasterServiceUrl
          }
        }
      }]
    });

    console.log('Sponsored transaction sent:', result);
    return result;
  } catch (error) {
    console.error('Sponsored transaction failed:', error);
    throw error;
  }
}

Multiple Sponsored Transactions

You can also batch multiple transactions and have them all sponsored:

async function sendMultipleSponsoredTransactions() {
  const sdk = createBaseAccountSDK({
    appName: 'Paymaster Demo',
    appLogoUrl: 'https://base.org/logo.png',
    appChainIds: [base.constants.CHAIN_IDS.baseSepolia],
  });

  const provider = sdk.getProvider();
  const cryptoAccount = await getCryptoKeyAccount();
  const fromAddress = cryptoAccount?.account?.address;

  const paymasterServiceUrl = process.env.NEXT_PUBLIC_PAYMASTER_PROXY_SERVER_URL;

  // Multiple calls in a single sponsored transaction
  const calls = [
    {
      to: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
      value: numberToHex(parseEther('0.001')),
      data: '0x' // Simple ETH transfer
    },
    {
      to: '0x742d35Cc6634C0532925a3b844Bc9e7595f6E456',
      value: numberToHex(parseEther('0.001')),
      data: '0x' // Another ETH transfer
    }
  ];

  const result = await provider.request({
    method: 'wallet_sendCalls',
    params: [{
      version: '1.0',
      chainId: numberToHex(base.constants.CHAIN_IDS.baseSepolia),
      from: fromAddress,
      calls: calls,
      capabilities: {
        paymasterService: {
          url: paymasterServiceUrl
        }
      }
    }]
  });

  return result;
}

Check Paymaster Capabilities

Before sending sponsored transactions, you can check if the wallet supports paymaster services:

async function checkPaymasterSupport() {
  const sdk = createBaseAccountSDK({
    appName: 'Paymaster Demo',
    appLogoUrl: 'https://base.org/logo.png',
    appChainIds: [base.constants.CHAIN_IDS.baseSepolia],
  });

  const provider = sdk.getProvider();
  const cryptoAccount = await getCryptoKeyAccount();
  const address = cryptoAccount?.account?.address;

  try {
    const capabilities = await provider.request({
      method: 'wallet_getCapabilities',
      params: [address]
    });

    const baseCapabilities = capabilities[base.constants.CHAIN_IDS.baseSepolia];
    
    if (baseCapabilities?.paymasterService?.supported) {
      console.log('Paymaster service is supported');
      return true;
    } else {
      console.log('Paymaster service is not supported');
      return false;
    }
  } catch (error) {
    console.error('Failed to check paymaster capabilities:', error);
    return false;
  }
}

That’s it! Base Account will handle the rest. If your Paymaster service is able to sponsor the transaction, in the UI Base Account will indicate to your user that the transaction is sponsored.