BLOG — Developer Tutorials

434 days ago

How to send your first gasless transaction with Gelato Relay

Send your first gasless tx in less than 10 minutes!

Creating a gasless user experience for your application will allow you to build awesome apps and offer your users the ability to send transactions without needing to have any native tokens to pay for gas.

Gelato Relay provides out-of-the-box authentication with flexible payment support.

Just answer two easy questions first and in the time it takes to make a coffee, your first gasless tx will be ready to execute!

Questions to ask yourself before starting

1. Do you require user authentication?

To authenticate a user, you need to know their original address. Since we're off-chain for gasless transactions, we will need to get the user address on-chain. Gelato Relay supports ERC2771 to utilize EIP-712 signatures which verify and recover the user’s address on-chain.

The msgSender() function is available via inheritance, allowing you to access the original msg.sender. In the context of relaying, if you try msg.sender, you'll get the address of Gelato’s Relay contract.

On your frontend, all you need to do is sign the request. Our demo app will use this flow for submitting a proposal in order to prevent double voting. There are some use cases where you may not need to authenticate users at all, like if you're building an app where anyone can create proposals, you won't need to to authenticate your users.

2. What is the funding strategy?

In a nutshell, the transactions can be funded one of two ways:

  • You can sponsor gas fees through 1Balance, a cross-chain pool where you can deposit funds and use them for any transaction on any chain. This corresponds to either using sponsoredCall or sponsoredCallERC2771.

Or

A more in-depth explanation of the payment options can be found here.

Note: We recommend using 1Balance for a more streamlined payment experience.

We’re going to keep things simple for demo purposes: for our first gasless transaction, we’ll pay using the target contract balance.

For our second gasless transaction, we’ll show you how to use 1Balance, which allows us to fund and create the SponsorKeys ourselves. You can check out the 1Balance beta here!

Two Steps to Best in Class UX

Once our funding strategy is finalized and we’ve decided whether we need Gelato's support for user authentication, there are two simple steps:

  • Prepare the target contract to communicate with the relayer inheriting one of Gelato’s helper contracts and

  • Build & send the transaction in your frontend

Using the following table, pick the relevant contracts and SDK methods for our use case:

Gelato Auth Payment Inheriting Contract SDK/API method
no User GelatoRelayContext relayWithSyncFee
yes User GelatoRelayContextERC2771 relayWithSyncFeeERC2771
no 1Balance n. a. relayWithSponsoredCall
yes¹ 1Balance ERC2771Context relayWithSponsoredCallERC2771

¹ A SponsorKey is required, visit Gelato 1Balance here

Step #1: Prepare the target contract to communicate with the relayer

For the target contract to execute our transaction, it may need some additional data depending on the payment method or the user authentication.

Here’s how we make the contract “aware” of what data is required:

Example target contract paying fees and anonymous users (no authentication)

Here we will inherit the GelatoRelayContext

// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import { GelatoRelayContext } from "@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
  
   ….
contract GaslessProposing is {
   …
 
 // @notice
 // @dev external only Gelato relayer
 // @dev transfer Fee to Geato with _transferRelayFee();
  function createProposal(bytes calldata payload) external onlyGelatoRelay {
    …
   _transferRelayFee();
    }
…
} 

With only three small changes, our contract is now “relayer aware”

  1. Importing the contract to inherit
import {GelatoRelayContext} from 
"@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
"@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
  1. Inheriting the contract
contract GaslessProposing is GelatoRelayContext
  1. Include the onlyGelatoRelay modifier and pay for the transaction
function createProposal(bytes calldata payload) external onlyGelatoRelay {
  …
   _transferRelayFee();
 }

Example target contract using 1Balance and authenticated users

Here we will inherit the ERC2771Context

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import {
   ERC2771Context
} from "@gelatonetwork/relay-context/contracts/vendor/ERC2771Context.sol";
 
contract GaslessVoting is ERC2771Context {
 …
 constructor() ERC2771Context(address(0xBf175FCC7086b4f9bd59d5EAE8eA67b8f940DE0d)) {
 }
 
 modifier onlyTrustedForwarder() {
   require(
     isTrustedForwarder(msg.sender),
     "Only callable by Trusted Forwarder"
   );
   _;
 }
 
 //  @notice voting proposal
 //  @dev function called by the relayer implementing the onlyTrusted Forwarder
 function votingProposal(bool positive) external onlyTrustedForwarder {
   address voter = _msgSender();
 
   _votingProposal(positive, voter);
 }

In the example above, we have transformed our contract into a “Relay aware contract” in just 3 simple steps.

  1. Importing the contract to inherit
import {  ERC2771Context} from "@gelatonetwork/relay-context/contracts/vendor/ERC2771Context.sol";
  1. Inheriting the ERC2771Context contract
contract GaslessProposing is  ERC2771Context
  1. Include the onlyTrustedForwarder modifier
modifier onlyTrustedForwarder() {  
       require(isTrustedForwarder(msg.sender),"Only callable by Trusted Forwarder");
   _;
  }
 function votingProposal(bool positive) external onlyTrustedForwarder {
     address voter = _msgSender();
   _votingProposal(positive, voter);
  }

As we can see in this snippet the original msg.sender of the transaction will be available in our contract, calling the method _msgSender()

Step #2: Build and Send the Transaction

Now that our contract is ready to receive the relayed transactions, our next step is to build the request to the relayer. For this we will use a different SDK method as described in the table above.

Example request paying fees and anonymous users

In this first example, we do not require support to authenticate the users, and the target contract will pay for the transaction. We will use the GelatoRelaySDK relayWithSyncFee method.

import { GelatoRelaySDK } from '@gelatonetwork/relay-sdk';
   const feeToken = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
   const { data } =
     await this.readGaslessProposing.populateTransaction.createProposal(payload);
 
   // populate the relay SDK request body
   const request = {
     chainId: 5, // Goerli in this case
     target: targetContract.address, // target contract address
     data: data!, // encoded transaction datas
     isRelayContext: true, // are we using context contracts
     feeToken: feeToken, // token to pay the relayer
   };
 
   // send relayRequest to Gelato Relay API
   const relayResponse = await GelatoRelaySDK.relayWithSyncFee(request);

Example request using 1Balance and authenticated users

 
     const { data } =
       await this.gaslessVoting.populateTransaction.votingProposal(value);
 
     const request = {
       chainId: 5, // Goerli in this case
       target: targetContract.Address, // target contract address
       data: data!, // encoded transaction datas
       user: userAddress!, //user sending the trasnaction
     };
 
     const sponsorApiKey = 'YOUR KEY';
 
     const relayResponse = await GelatoRelaySDK.relayWithSponsoredCallERC2771(
       request,
       provider,
       sponsorApiKey
     );

When using the method relayWithSponsoredCallERC2771 we will be prompted to sign our request so that our ERC2771 backend can decode the signature.

Conclusion

By helping developers augment their smart contracts and make them gasless, Gelato Relay removes multiple friction points for end-users. By making your onboarding experience seamless with gasless transactions, users can skip the tedious process of obtaining network native tokens and instead focus on enjoying your application.

About Gelato

Gelato is a Web3 Cloud Platform empowering developers to create automated, gasless, and off-chain-aware Layer 2 chains and smart contracts. Over 400 web3 projects rely on Gelato for years to facilitate millions of transactions in DeFi, NFTs, and gaming.

  • Gelato RaaS: Deploy your own tailor-made ZK or OP L2 chains in a single click with native Account Abstraction and all Gelato middleware baked in.

  • Web3 Functions: Connect your smart contracts to off-chain data & computation by running decentralized cloud functions.

  • Automate: Automate your smart contracts by executing transactions automatically in a reliable, developer-friendly & decentralized manner.

  • Relay: Give your users access to reliable, robust, and scalable gasless transactions via a simple-to-use API.

  • Account Abstraction SDK: Gelato has partnered with Safe, to build a fully-fledged Account Abstraction SDK, combining Gelato's industry's best gasless transaction capabilities, with the industry's most secure smart contract wallet.

Subscribe to our newsletter and turn on your Twitter notifications to get the most recent updates about the Gelato ecosystem! If you are interested in being part of the Gelato team and building the future of the Internet browse the open positions and apply here.