Gasless NFT Minting Using USDC: Powered by Gelato Relay

Gelato Team

Jun 20, 2023

Non-Fungible Tokens (NFTs) have reshaped how we perceive and trade digital assets. But one barrier still hinders mainstream adoption — the need for a native gas token to transact on the network.

By combining Gelato Relay with USDC, this barrier can be removed. In this guide, we’ll show you how to integrate gasless minting into your next NFT project.

How it works

The user signs an off-chain permit signature that allows the NFT contract to spend a specified amount of USDC.

The arguments and signature are submitted to Gelato using the Relay SDK, and executed on-chain by a relayer.

The mint function uses the signature to permit itself to spend USDC: one USDC is used to pay for the NFT, and some covers the relay fee. The permitted amount includes overhead for gas price fluctuations — the full amount is rarely spent.

Requirements

  1. Permit spending of USDC using an off-chain signature

  2. Compensate the relayer by paying a fee

By leveraging USDC permit signatures and Gelato Relay, we can enable a truly gasless experience. Since relayers put transactions on-chain, they incur a gas cost that must be compensated — either by sponsoring via 1Balance or by paying synchronously within the transaction. The latter lets us use USDC to cover both the NFT and the relay fee in the same transaction.

Prerequisites

  • Install Node.js, NPM, and Git

  • Set up a Web3 wallet such as MetaMask

  • Configure an RPC provider

Your Dev Environment

Find the full source code and deployment instructions on GitHub.

Code Explanation

The NFT contract is a typical ERC721. The deployer specifies the payment token (USDC) and price. Users call the mint function, which transfers payment tokens and mints the NFT. The supply is incremented to set token IDs.

Traditional NFT Contract

// Traditional NFT minting; native token required for gas
contract NFT is ERC721 {
    uint256 public price;
    uint256 public supply;
    ERC20 public token;

    constructor(ERC20 _token, uint256 _price) ERC721("NFT", "NFT") {
        price = _price;
        token = _token;
    }

    function mint(address to) external {
        token.transferFrom(to, address(this), price);
        _mint(to, supply++);
    }
}

Gasless NFT Contract

To support gasless minting, we modify mint to accept permit arguments. It uses onlyGelatoRelay from GelatoRelayContext to ensure only relayers can call it. This prevents malicious use and gives access to fee data and utilities.

// Gasless NFT minting; no native token required
contract GaslessNFT is ERC721, GelatoRelayContext {
    uint256 public price;
    uint256 public supply;
    ERC20Permit public token;

    constructor(ERC20Permit _token, uint256 _price)
        ERC721("Gasless NFT", "GNFT")
    {
        price = _price;
        token = _token;
    }

    function mint(
        address to, 
        uint256 amount, 
        uint256 deadline, 
        uint8 v,
        bytes32 r, 
        bytes32 s
    ) external onlyGelatoRelay {
        require(address(token) == _getFeeToken(),
            "GaslessNFT.mint: incorrect fee token");

        token.permit(to, address(this), amount, deadline, v, r, s);
        token.transferFrom(to, address(this), price);

        uint256 fee = _getFee();
        uint256 maxFee = amount - price;

        require(fee <= maxFee,
            "GaslessNFT.mint: insufficient fee");

        token.transferFrom(to, _getFeeCollector(), fee);

        _mint(to, supply++);
    }
}

Note: ERC-2771 is usually recommended, but here authentication is handled by USDC’s ERC-2612 permit implementation.

Frontend Implementation

The frontend estimates the relay fee and signs a permit to allow the NFT contract to spend USDC (price + fee). It then builds the request and submits it with callWithSyncFee via the Relay SDK.

const purchase = async () => {
   const wallet = new ethers.BrowserProvider(window.ethereum);
   const signer = await wallet.getSigner();

   const amount = price + fee;
   const deadline = ethers.MaxUint256;

   const { chainId } = await provider.getNetwork();

   const sig = await sign(signer, token, amount, nft, deadline, chainId);
   const { v, r, s } = sig;

   const { data } = await nft.mint.populateTransaction(
      signer.address, amount, deadline, v, r, s
   );

   const request: CallWithSyncFeeRequest = {
      chainId: chainId.toString(),
      target: nft.target.toString(),
      feeToken: token.target.toString(),
      isRelayContext: true,
      data: data
   };

   const { taskId } = await relay.callWithSyncFee(request);
};

Note: For clarity, certain checks are omitted. See the full repo here.

Conclusion

Gasless NFT minting enhances UX and paves the way for mainstream adoption. By leveraging USDC permit signatures and Gelato Relay, users can mint NFTs without needing native gas tokens — secure, seamless, and trust minimized.

About Gelato Relay

Gelato abstracts away blockchain complexities, providing secure gasless transactions and a smooth onboarding experience.

Explore Relay alongside other Gelato services like Web3 Functions, Automate, and the Account Abstraction SDK.

Join our developer community on Discord!

Ready to build?

Start with a testnet, launch your mainnet in days, and scale with industry-leading UX.

Ready to build?

Start with a testnet, launch your mainnet in days, and scale with industry-leading UX.

Ready to build?

Start with a testnet, launch your mainnet in days, and scale with industry-leading UX.

Ready to build?

Start with a testnet, launch your mainnet in days, and scale with industry-leading UX.