Going Gasless and sponsoring transactions with Gelato

Going Gasless and sponsoring transactions with Gelato

⚠️ Warning: This guide currently uses v4 of the Connect SDK. For v5 (latest) code snippets, please check out our documentation while this guide is being updated. ⚠️

Summary: In this tutorial, we'll create two React components called CounterApp and GaslessNFTApp which both interact with a smart contract deployed on the Polygon network. The purpose of these components is to allow users to either increment a counter value stored in the smart contract or mint an NFT to their address without paying gas fees for the transaction, using the Gelato Relay SDK and Thirdweb's React SDK.

Introduction

You might have heard all the hype about account abstraction, such as ERC 4337, and some of the benefits it will bring to users and developers: network abstraction, signature abstraction, and gas abstraction, to name a few.

However, Gas abstraction is where we will focus today. Gas transactions are a massive friction point for onboarding new users. To get someone onboarded, who is entirely new to Web3, they have to create a wallet and possess a native token just to get started with their first transaction. If you are a developer with a new protocol, game, or NFT project, this can be where a lot of potential users drop off; as usual, they have to buy the native token with a fiat on-ramp, and usually, that requires KYC.

How can we get rid of this tedious step? Using Gelato Relay, you can abstract gas away today without implementing any support for EIPs such as ERC-4337.

This guide will walk you through a demo app, which utilizes the Gelato Relay SDK and our innovative 1Balance payment system, allowing you to sponsor transactions for users so they don't have to worry about gas. This feature is available on all networks Gelato supports. This demo app will utilize Thirdweb's React SDK for wallet connections, pulling information from on-chain, and for NFT contract deployment using the standard "NFTDrop" template contract.

Prerequisites

Before you start, make sure you have the following:

  • A basic understanding of React, TypeScript, and smart contracts.
  • Node.js and npm installed on your machine.
  • A code editor, such as Visual Studio Code.

Step 1: Set up the project and install dependencies

Clone the project from GitHub:

git clone https://github.com/gelatodigital/gelato-thirdweb-relay

Change to the project directory and install the necessary dependencies:

cd gelato-thirdweb-relay
npm install

Step 2: Run the project and try it out in your browser

npm start

If you navigate over to http://localhost:3000/ in your browser, you should see the following:

gasless-thirdweb-relay frontend

Step 3: Connect your wallet, and connect to Polygon mainnet

Connect your wallet with thirdweb's "Connect Wallet" button in the top right.

Once connected, you will see the 'Increment' button

After that, you are ready to click increment. Once you click increment, you will see some information polled from Gelato Relay's Status API. When you submit a request to our relayer, we designate this as a Task ID, pending through our task queue. Using the status API, you can see its status and get information on its execution.

"ExecSuccess"!

Did you notice that you didn't have to sign any transaction or pay for gas at all, and yet you just interacted with and updated the state on the blockchain? That's a gasless transaction - let's look into how exactly this works.

What just happened?

Let's take a look at the code and specifically at src/components/CounterApp.tsx.

At line 34, there is a function called sendRelayRequest:

  const sendRelayRequest = async () => {
    // update state
    setInitiated(true);
    setPopup(false);
    setTaskId("");
    setTxHash("");
    setStartTime(0);
    setTaskStatus("Loading...");

    // instantiating Gelato Relay SDK
    const relay = new GelatoRelay();

    // connecting to contract through front-end provider
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(target, counterABI.abi, signer);

    // relay request parameters
    const feeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
    const { data } = await contract.populateTransaction.increment();

    if (!props.chainId || !data) return;

    const request: CallWithSyncFeeRequest = {
      chainId,
      target,
      data,
      feeToken,
      isRelayContext: true,
    };

    const relayResponse = await relay.callWithSyncFee(request);
    setTaskId(relayResponse.taskId);
    setStartTime(Date.now());
  };
  

We can ignore the state updates for now but let's break down how to use the Gelato Relay SDK.

The line const relay = new GelatoRelay(); creates a new instance of the GelatoRelay class from the Gelato Relay SDK. The Gelato Relay SDK provides a simple way to interact with the Gelato Relay service, which enables gasless transactions on supported networks like Polygon.

We still have to connect to the blockchain via your frontend wallet in order to pull information from chain such as the counter value, and also retrieve the counter smart contract's ABI.

  const provider = new ethers.providers.Web3Provider(window.ethereum);
  const signer = provider.getSigner();
  const contract = new ethers.Contract(target, counterABI.abi, signer);
  

We need the ABI to generate the "payload" telling Gelato Relay which function to call on the target smart contract, and with what arguments:

const { data } = await contract.populateTransaction.increment();

Once we have the payload with data, we can build a simple sponsored relay request:

const request: CallWithSyncFeeRequest = {
    chainId,
    target,
    data,
    feeToken,
    isRelayContext: true,
};
   

This snippet is a CallWithSyncFeeRequest, allowing the target smart contract to pay for and sponsor the gas in various tokens. You can read more about callWithSyncFee here. Thirdweb provides hooks such as useChainId() to automatically grab the chainID from the connected wallet so that it is fetched automatically on line 12.

To link the sendRelayRequest function to the increment button, we add an onClick parameter to the displayed button:

<button onClick={sendRelayRequest}>

How can I sponsor transactions?

What if you'd like to sponsor all transactions for all your users across all supported networks with just 1 API key?

1Balance Sponsoring

Head over to https://relay.gelato.network and after connecting your wallet, you'll see our dashboard.

Login into 1Balance by clicking "1Balance login," You will log in with your wallet using a signature. This process is just for authentication and does not interact on-chain.

Once logged in, you can deposit funds used to subsidize the gas costs for any transactions you request via Gelato Relay. Gelato also takes a small fee on top of gas, which you can read more about in the Gelato documentation. For all mainnet relaying, i.e., Ethereum, Polygon, Optimism, and more, your 1Balance is on Polygon using USDC. Once you deposit USDC, this new balance will be used across all your transactions across all Gelato-supported networks. You don't have to worry about keeping track of multiple balances across all networks you would like to support.

However, before going to mainnets, if you'd like to test out 1Balance, you can click "Testnets" and deposit gETH on Goerli to support all transactions across Goerli, Optimism Goerli, Arbitrum Goerli, Base Goerli, and Mumbai.

Once you have deposited, you can create a new app.

Paste in your target contract address (or use the demo counter contract on Polygon at 0x730615186326cF8f03E34a2B49ed0f43A38c0603 and network and enable the functions you want to call using Gelato Relay. You can also allow any contract on that network by hitting the "Any Contract" toggle.
Once you create your app, you can grab your API key. With the API key, you can now change the request code in the sendRelayRequest function like the snippets below:
First, build the struct for the SponsoredCallRequest:

const request: SponsoredCallRequest = {
    chainId,
    target,
    data,
};

Then you can modify the relay request itself to:

const relayResponse = await relay.sponsoredCall(request, apiKey);

Remember that API keys can be leaked from the front end. For demo purposes, this is indicative of an example demo hosted locally. To keep your API key secret while using it in your front end, create a backend server that acts as a proxy between your front end and the API provider. This way, the API key remains hidden from the frontend code, and only the server can access it.

Now we can build our own app.

How to make your own app

With React's modularity, creating your own component is super simple. You can create your own by duplicating CounterApp.tsx and going from there, but for this tutorial, we'll look into GaslessNFTApp.tsx in the example repo.

GaslessNFTApp.tsx uses a different modality of payment where the user signs a message to confirm they want a transaction to come through. This practice follows the ERC-2771 standard; you can read more about it in Gelato's documentation.

Let's start by enabling this app in the AppContainer.tsx file located in components.

import CounterApp from "./apps/CounterApp";
// import GaslessNFTApp from "./apps/GaslessNFTApp";

const AppContainer = () => {
  return (
    <div>
      <CounterApp />
      {/* <GaslessNFTApp /> */}
    </div>
  );
};

export default AppContainer;

Next, uncomment line 2 and line 8 to activate the GaslessNFTApp in the front end. Now, if you are still running the development server, you can navigate to http://localhost:3000/ and see the new app (otherwise, run npm start again).

The Gasless NFT App has been added below.

If you click "Click NFT," nothing will happen yet, as you need to add a target contract address and a 1Balance sponsor API key. If you'd like to use the demo NFT smart contract on Polygon, please use the address 0x730615186326cF8f03E34a2B49ed0f43A38c0603.

Otherwise, read below to see how to deploy your NFT contract using thirdweb.

Deploy your NFT contract using thirdweb's dashboard.

This NFT mint is gasless, and the NFT contract is deployed using thirdweb's dashboard using the template contract "NFTDrop" found here. Thirdweb makes it super easy to deploy an NFT contract and edit claim conditions, prices, and more. You can read a complete guide for deploying this NFT contract via thirdweb's Deploy an ERC721A NFT Smart Contract guide.

The most important part of setting up is the claim conditions post-deployment:

For this demo, the NFTs are free and unlimited (though in practice, there are only 20 different pictures uploaded for the actual NFTs themselves).

Once deployed, grab the contract address and paste it into line 20 on GaslessNFTApp.tsx in the target variable:

const target = "PASTE_CONTRACT_ADDRESS_HERE"

Thirdweb makes it super easy to connect to a contract and pull out data from that contract:

  // contract object instantiate
  const { contract, isLoading } = useContract(target, "nft-drop");
  const { data: nfts, refetch } = useNFTs(contract, { start: 0, count: 20 });

Importing an ABI here is unnecessary because thirdweb handles ABI management for you. Furthermore, Thirdweb's React SDK gives custom hooks like useNFTs which is a hook to query all NFTs associated with a smart contract. This process is all done in one line of code, saving so much time and headaches associated with querying.

Next up, paste your generated 1Balance sponsor API key into line 79:

 const sponsorAPIkey = "PASTE_SPONSOR_API_KEY_HERE";

Once both are ready, you can head to the front end and click "Claim NFT" Your NFT will be minted and sent to the wallet connected, all without gas.

"ExecSuccess!"

Conclusion

You've now learned how to create your app with gasless transaction support for simple state updates using CounterApp and a gasless NFT drop using GaslessNFTApp utilizing Gelato Relay's sponsored transactions and Gelato 1Balance which heavily utilizes thirdweb's React SDK to connect and pull data from smart contracts very quickly and efficiently within React-based front ends.