Learn how to leverage the Base Paymaster for seamless, gasless transactions on the Coinbase Cloud Developer Platform.
Base transaction fees are typically less than a penny, but the concept of gas can still be confusing for new users and lead to poor user experience when users don’t have gas funds in their wallet. You can abstract this away and improve your UX by using the Base Paymaster. The Paymaster allows you to:
Batch multi-step transactions
Create custom gasless experiences
Sponsor up to $15k monthly on mainnet (unlimited on testnet)
A Coinbase Cloud Developer Platform Account
If not, sign up on the CDP site. Once you have your account, you can manage projects and utilize tools like the Paymaster.
Familiarity with Smart Accounts and ERC 4337
Smart Accounts are the backbone of advanced transaction patterns (e.g., bundling, sponsorship). If you’re new to ERC 4337, check out external resources like the official EIP-4337 explainer before starting.
Foundry Foundry is a development environment, testing framework, and smart contract toolkit for Ethereum. You’ll need it installed locally for generating key pairs and interacting with smart contracts.
Testnet vs. MainnetIf you prefer not to spend real funds, you can switch to Base Sepolia (testnet). The steps below are conceptually the same. Just select Base Sepolia in the Coinbase Developer Platform instead of Base Mainnet, and use a contract deployed on Base testnet for your allowlisted methods.
Scroll down to the Per User Limit section. You can set:
Dollar amount limit or number of UserOperations per user
Limit cycles that reset daily, weekly, or monthly
For example, you might set:
max USD to $0.05
max UserOperation to 1
This means each user can only have $0.05 in sponsored gas and 1 user operation before the cycle resets.
Limit CyclesThese reset based on the selected cadence (daily, weekly, monthly).
Next, set the Global Limit. For example, set this to $0.07 so that once the entire paymaster has sponsored $0.07 worth of gas (across all users), no more sponsorship occurs unless you raise the limit.
Create a .env file in the sponsored_transactions directory. In the .env, you’ll add the rpcURL for your paymaster and the private keys for your accounts:
[Find your Paymaster & Bundler endpoint]The Paymaster & Bundler endpoint is the URL for your Coinbase Developer Platform (CDP) Paymaster.
This was saved in the previous section and follows this format: https://api.developer.coinbase.com/rpc/v1/base/<SPECIAL-KEY>
Navigate to the Paymaster Tool and select the Configuration tab at the top of the screen to obtain your RPC URL.
[Secure your endpoints]You will create a constant for our Paymaster & Bundler endpoint obtained from cdp.portal.coinbase.com. The most secure way to do this is by using a proxy. For the purposes of this demo, hardcode it into our index.js file. For product, we highly recommend using a proxy service.
Below is a full example of how you might structure index.js.
Report incorrect code
Copy
Ask AI
// --- index.js ---// 1. Import modules and environment variablesimport 'dotenv/config';import { http, createPublicClient, encodeFunctionData } from 'viem';import { base } from 'viem/chains';import { createSmartAccountClient } from 'permissionless';import { privateKeyToSimpleSmartAccount } from 'permissionless/accounts';import { createPimlicoPaymasterClient } from 'permissionless/clients/pimlico';// 2. Retrieve secrets from .env// Highlight: environment variables for paymaster, private keysconstrpcUrl =process.env.PAYMASTER_RPC_URL; // highlightconstfirstPrivateKey =process.env.PRIVATE_KEY_1; // highlightconstsecondPrivateKey =process.env.PRIVATE_KEY_2; // highlight// 3. Declare Base addresses (entrypoint & factory)constbaseEntryPoint = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789';constbaseFactoryAddress = '0x15Ba39375ee2Ab563E8873C8390be6f2E2F50232';// 4. Create a public client for BaseconstpublicClient =createPublicClient({chain:base,transport:http(rpcUrl),});// 5. Setup Paymaster clientconstcloudPaymaster =createPimlicoPaymasterClient({chain:base,transport:http(rpcUrl),entryPoint:baseEntryPoint,});// 6. Create Smart Accounts from the private keysasync functioninitSmartAccounts() { constsimpleAccount = awaitprivateKeyToSimpleSmartAccount(publicClient, {privateKey:firstPrivateKey,factoryAddress:baseFactoryAddress,entryPoint:baseEntryPoint, }); constsimpleAccount2 = awaitprivateKeyToSimpleSmartAccount(publicClient, {privateKey:secondPrivateKey,factoryAddress:baseFactoryAddress,entryPoint:baseEntryPoint, }); // 7. Create SmartAccountClient for each constsmartAccountClient =createSmartAccountClient({account:simpleAccount,chain:base,bundlerTransport:http(rpcUrl),middleware: {sponsorUserOperation:cloudPaymaster.sponsorUserOperation, }, }); constsmartAccountClient2 =createSmartAccountClient({account:simpleAccount2,chain:base,bundlerTransport:http(rpcUrl),middleware: {sponsorUserOperation:cloudPaymaster.sponsorUserOperation, }, }); return { smartAccountClient, smartAccountClient2 };}// 8. ABI for the NFT contractconstnftAbi = [ // ... // truncated for brevity];// 9. Example function to send a transaction from a given SmartAccountClientasync functionsendTransaction(client, recipientAddress) { try { // encode the "mintTo" function call constcallData =encodeFunctionData({abi:nftAbi,functionName: 'mintTo',args: [recipientAddress], // highlight: specify who gets the minted NFT }); consttxHash = awaitclient.sendTransaction({account:client.account,to: '0x83bd615eb93eE1336acA53e185b03B54fF4A17e8', // address of the NFT contractdata:callData,value: 0n, });console.log(`✅ Transaction successfully sponsored for ${client.account.address}`);console.log(`🔍 View on BaseScan: https://basescan.org/tx/${txHash}`); } catch (error) {console.error('Transaction failed:', error); }}// 10. Main flow: init accounts, send transactions(async () => { const { smartAccountClient, smartAccountClient2 } = awaitinitSmartAccounts(); // Send a transaction from the first account awaitsendTransaction(smartAccountClient, smartAccountClient.account.address); // Send a transaction from the second account // For variety, let's also mint to the second account's own address awaitsendTransaction(smartAccountClient2, smartAccountClient2.account.address);})();
Now that the code is implemented, lets run it:
Run this via node index.js from your project root.
Report incorrect code
Copy
Ask AI
node index.js
You should see a “Transaction successfully sponsored” output.To confirm that your spend policies are correctly in place, try running the script again. If your Paymaster settings are strict (e.g., limit 1 transaction per user), the second time you run the script, you may get a “request denied” error, indicating the policy is working.
Set up and configured a Base Paymaster on the Coinbase Developer Platform.
Allowlisted a contract and specific function (mintTo) for sponsorship.
Established per-user and global sponsorship limits to control costs.
Demonstrated the sponsorship flow with Smart Accounts using permissionless, viem, and Foundry-generated private keys.
This approach can greatly improve your onchain app’s user experience by removing gas friction. For more complex sponsorship schemes (like daily or weekly cycles), simply tweak your per-user and global limit settings in the Coinbase Developer Platform.