Back to Guides
tutorial8 min readDecember 1, 2024GasX Team

How to Add a Gasless Transaction Button to Your Next.js dApp

Step-by-step tutorial to implement gasless transactions using ERC-4337 account abstraction in your Next.js application. Complete code examples with GasX integration.

gasless transactionsNext.jsERC-4337account abstractionReactWeb3tutorial

How to Add a Gasless Transaction Button to Your Next.js dApp

Gas fees are the #1 barrier to Web3 adoption. New users abandon transactions when they see they need ETH just to interact with your dApp. This tutorial shows you how to eliminate that friction with gasless transactions.

What You'll Build

By the end of this tutorial, you'll have a working "Gasless Mint" button that:

  • Lets users mint NFTs without paying gas
  • Works with any ERC-4337 compatible wallet
  • Integrates with GasX for gas sponsorship

Prerequisites

  • Next.js 14+ project with App Router
  • Basic understanding of React hooks
  • A GasX account (free tier available)

Step 1: Install Dependencies

First, install the required packages:

pnpm add wagmi viem @tanstack/react-query permissionless

These packages provide:

  • wagmi: React hooks for Ethereum
  • viem: TypeScript Ethereum library
  • permissionless: ERC-4337 account abstraction utilities

Step 2: Configure Your Providers

Create a providers file to set up wagmi with account abstraction support:

// app/providers.tsx
'use client';

import { WagmiProvider, createConfig, http } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { arbitrum, optimism, base } from 'wagmi/chains';

const config = createConfig({
  chains: [arbitrum, optimism, base],
  transports: {
    [arbitrum.id]: http(),
    [optimism.id]: http(),
    [base.id]: http(),
  },
});

const queryClient = new QueryClient();

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  );
}

Step 3: Create the Gasless Transaction Hook

This is where the magic happens. Create a custom hook that handles gasless transactions:

// hooks/useGaslessTransaction.ts
'use client';

import { useState } from 'react';
import { encodeFunctionData, type Hex } from 'viem';
import { useAccount } from 'wagmi';

interface GaslessTransactionParams {
  to: Hex;
  data: Hex;
  value?: bigint;
}

export function useGaslessTransaction() {
  const { address } = useAccount();
  const [isPending, setIsPending] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const sendGaslessTransaction = async (params: GaslessTransactionParams) => {
    if (!address) {
      throw new Error('Wallet not connected');
    }

    setIsPending(true);
    setError(null);

    try {
      // Call GasX API to sponsor the transaction
      const response = await fetch('/api/sponsor', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          userAddress: address,
          target: params.to,
          callData: params.data,
          value: params.value?.toString() || '0',
        }),
      });

      if (!response.ok) {
        throw new Error('Sponsorship request failed');
      }

      const { userOpHash } = await response.json();
      return userOpHash;
    } catch (err) {
      setError(err instanceof Error ? err : new Error('Unknown error'));
      throw err;
    } finally {
      setIsPending(false);
    }
  };

  return {
    sendGaslessTransaction,
    isPending,
    error,
  };
}

Step 4: Build the Gasless Button Component

Now create a reusable button component:

// components/GaslessButton.tsx
'use client';

import { useState } from 'react';
import { encodeFunctionData } from 'viem';
import { useGaslessTransaction } from '@/hooks/useGaslessTransaction';
import { Button } from '@/components/ui/button';

const NFT_CONTRACT = '0x...'; // Your NFT contract address
const NFT_ABI = [
  {
    name: 'mint',
    type: 'function',
    inputs: [{ name: 'to', type: 'address' }],
    outputs: [],
  },
] as const;

export function GaslessMintButton() {
  const { sendGaslessTransaction, isPending } = useGaslessTransaction();
  const [txHash, setTxHash] = useState<string | null>(null);

  const handleMint = async () => {
    const data = encodeFunctionData({
      abi: NFT_ABI,
      functionName: 'mint',
      args: ['0x...'], // User's address
    });

    const hash = await sendGaslessTransaction({
      to: NFT_CONTRACT,
      data,
    });

    setTxHash(hash);
  };

  return (
    <div>
      <Button onClick={handleMint} disabled={isPending}>
        {isPending ? 'Minting...' : 'Mint NFT (Gasless)'}
      </Button>
      {txHash && (
        <p className="text-sm text-muted-foreground mt-2">
          Transaction: {txHash}
        </p>
      )}
    </div>
  );
}

Step 5: Set Up the API Route

Create an API route to handle sponsorship requests:

// app/api/sponsor/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const body = await request.json();

  // Call GasX API to create sponsored UserOperation
  const response = await fetch('https://api.gasx.io/v1/sponsor', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.GASX_API_KEY}`,
    },
    body: JSON.stringify({
      campaignId: process.env.GASX_CAMPAIGN_ID,
      ...body,
    }),
  });

  const data = await response.json();
  return NextResponse.json(data);
}

Step 6: Configure GasX Campaign

  1. Go to GasX Dashboard
  2. Create a new campaign
  3. Set your gas budget and eligible contracts
  4. Copy your Campaign ID and API Key
  5. Add them to your .env.local:
GASX_API_KEY=your_api_key_here
GASX_CAMPAIGN_ID=your_campaign_id_here

Testing Your Integration

Run your development server:

pnpm dev

Try clicking the "Mint NFT (Gasless)" button. The transaction should go through without the user paying any gas!

Best Practices

1. Rate Limiting

Implement rate limiting to prevent abuse:

// Use a rate limiter like upstash/ratelimit
import { Ratelimit } from '@upstash/ratelimit';

2. Allowlist Contracts

Only sponsor transactions to known contracts to prevent exploitation.

3. Set Budget Caps

Configure daily and per-user limits in your GasX campaign settings.

4. Monitor Usage

Use the GasX analytics dashboard to track spending and identify patterns.

Common Issues

"Sponsorship request failed"

  • Check your API key is valid
  • Verify the contract is allowlisted
  • Ensure your campaign has remaining budget

"Wallet not connected"

  • Make sure the user has connected their wallet before calling the function

Next Steps

  • Add transaction status tracking with webhooks
  • Implement multi-chain support
  • Create a custom paymaster for advanced use cases

Conclusion

Gasless transactions dramatically improve user experience in Web3. With GasX and ERC-4337, you can sponsor gas fees for your users with just a few lines of code.

Ready to go live? Create your GasX campaign now and start onboarding users without the gas fee friction.

Ready to eliminate gas friction?

Create your first gas sponsorship campaign in under 5 minutes. No coding required.

Create Campaign