Making Your First Batch ERC-20 Token Payment on Request Network: A Developer's Guide

Making Your First Batch ERC-20 Token Payment on Request Network: A Developer's Guide

Introduction

In this guide, we’ll explore how to integrate the Request Network into your dApp. We'll cover creating ERC-20 token requests using the Request Network and demonstrate how to batch-pay these requests efficiently in a single transaction.

What is Request Network?

Request Network is a blockchain-based financial infrastructure platform designed to empower web3 builders with innovative financial tools and services. The platform provides a comprehensive suite of solutions that enable developers to create advanced financial applications with improved data ownership, compliance, and traceability.

Request price today, REQ to USD live price, marketcap and chart |  CoinMarketCap

In this Guide

  • Creating an ERC20 request

  • Fetching and Tracking requests

  • Paying the requests

  • Batch paying the requests

Prerequisites

Before we dive into the code, make sure you have:

  • A Web3 wallet (like MetaMask)

  • Basic understanding of React and blockchain development

  • Familiarity with Ethereum and smart contract interactions

  • The Request Network SDKs installed

To be specific you need the following SDKs to use this guide completely.

@requestnetwork/payment-processor
@requestnetwork/request-client.js
@requestnetwork/web3-signature

Code and Resources

The guide is based on the project, Request Tasks submitted for the end-of-year hackathon.

Github: https://github.com/AnoyRC/request_tasks

Request Tasks is live at: https://www.requesttasks.xyz/

Getting Started

In this guide, we’ll skip the steps for setting up a wallet connection and installing essential packages like Wagmi and Ethers.

Important Note: Request Network currently does not support Ethers v6.

Before diving into request creation, ensure you have the following details ready:

Creating an ERC-20 Request

Let’s create an ERC-20 payment request which the payee himself signs.

Create a Web3SignatureProvider who will sign the request.

import { Web3SignatureProvider } from "@requestnetwork/web3-signature";

// for EthersV5
const web3SignatureProvider = new Web3SignatureProvider(provider);

// for Wagmi/Viem
const { data: walletClient } = useWalletClient();
const web3SignatureProvider = new Web3SignatureProvider(walletClient);

Initialize RequestNetwork to set up a connection with the request network node.

import { RequestNetwork } from "@requestnetwork/request-client.js";

const requestClient = new RequestNetwork({
   nodeConnectionConfig: {
      baseURL: "https://sepolia.gateway.request.network/",
   },
   signatureProvider: web3SignatureProvider,
});

Create a request parameter that is required to sign.

    import {Types, Utils} from "@requestnetwork/request-client.js";    

    // Address of payee
    const payeeIdentity = "";
    // Address of payer
    const payerIdentity = "";
    // If you want to impose extra fees
    const feeAmount = "0";
    const feeRecipient = payerIdentity;

    const requestCreateParameters = {
      requestInfo: {
        // The currency in which the request is denominated
        currency: {
          type: Types.RequestLogic.CURRENCY.ERC20,
          // USDC Token Contract
          value: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
          network: "sepolia",
        },

        // Sending 2 USDC, Amount * 10 ^ decimals
        expectedAmount: (2 * 10 ** 6).toFixed(0),

        // The payee identity. Not necessarily the same as the payment recipient.
        payee: {
          type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
          value: payeeIdentity,
        },

        // The payer identity. If omitted, any identity can pay the request.
        payer: {
          type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
          value: payerIdentity,
        },

        // The request creation timestamp.
        timestamp: Utils.getCurrentTimestampInSecond(),
      },

      // The paymentNetwork is the method of payment and related details.
      paymentNetwork: {
        id: Types.Extension.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT,
        parameters: {
          paymentNetworkName: "sepolia",
          paymentAddress: payeeIdentity,
          feeAddress: feeRecipient,
          feeAmount: feeAmount,
        },
      },

      // The contentData can contain anything.
      contentData: {
        topic: "guide",
        platform: "Hashnode",
      },

      // The identity that signs the request, either payee or payer identity.
      signer: {
        type: Types.Identity.TYPE.ETHEREUM_ADDRESS,
        value: payeeIdentity,
      },
    };

After setting the request parameter, we are ready to create a request.

const request = await requestClient.createRequest(requestCreateParameters);

const confirmedRequestData = await request.waitForConfirmation();

const requestId = confirmedRequestData.requestId

Voila! You have created your first request on Request Network :)

Celebrity gif. Rodney Dangerfield dressed as a car mechanic with his hand making an OK sign, bobbing back and forth while laughing.

Fetching and Tracking Request

Let’s fetch and check the status of the request.

import { RequestNetwork } from "@requestnetwork/request-client.js";

const requestId = "";    

const requestClient = new RequestNetwork({
    nodeConnectionConfig: {
      baseURL: "https://sepolia.gateway.request.network/",
    },
});

const request = await requestClient.fromRequestId(requestId);
let requestData = request.getData();

You can track if the request is paid or not too.

if (requestData.balance?.balance >= requestData.expectedAmount) {
    console.log("Request is Paid!!");
} else {
    console.log("Request is not paid!");
}

Paying the requests

Before paying the request, we need the requestData.

import { RequestNetwork } from "@requestnetwork/request-client.js";

const requestId = "";    

const requestClient = new RequestNetwork({
    nodeConnectionConfig: {
      baseURL: "https://sepolia.gateway.request.network/",
    },
});

const request = await requestClient.fromRequestId(requestId);
let requestData = request.getData();

To pay the requests, we need the signer. For this, only EtherV5 Provider & Signer is supported. Check out: https://wagmi.sh/react/guides/ethers to convert walletClient to provider and signer if you are using Wagmi/Viem.

Request Network provides in-built SDK functions that check for ERC-20 approvals and execute if you haven’t approved your ERC-20 tokens.

import { approveErc20,
      hasErc20Approval,
      hasSufficientFunds } from "@requestnetwork/payment-processor";

const _hasSufficientFunds = await hasSufficientFunds({
    request: requestData,
    address: requestData.payer.value,
    providerOptions: {
       provider: provider,
    },
});

if (!_hasSufficientFunds) {
    throw new Error("Insufficient funds");
    return;
}

const _hasErc20Approval = await hasErc20Approval(
    requestData,
    requestData.payer.value,
    signer
);

if (!_hasErc20Approval) {
    const approvalTx = await approveErc20(requestData, signer);
    await approvalTx.wait(2);
}

Finally, let’s pay the request.

import { payRequest } from "@requestnetwork/payment-processor";

const paymentTx = await payRequest(requestData, signer);
await paymentTx.wait(2);

Now, we can check if the request is confirmed by the Request Network node.

while (requestData.balance?.balance < requestData.expectedAmount) {
    requestData = await request.refresh();
    await new Promise((resolve) => setTimeout(resolve, 1000));
}

console.log("Request is now comfirmed and paid");

Congratulations! You have finally paid your first request :)

Movie gif. Leonardo DiCaprio as Jay Gatsby in The Great Gatsby raises a champagne glass in a celebratory toast as fireworks explode behind him.

Batch paying the requests

Handling multiple ERC-20 token payment requests individually can be inefficient and costly due to higher gas fees and time-consuming execution. By batching these payments, you can streamline the process, reduce gas costs, and execute all requests in a single transaction.

For batching requests, we need to modify the user's payload. Let’s convert requestData to EnrichedRequests.

import { RequestNetwork, Types } from "@requestnetwork/request-client.js";

const requestClient = new RequestNetwork({
    nodeConnectionConfig: {
      baseURL: "https://sepolia.gateway.request.network/",
    },
});

// Multiple RequestIds for multiple requests
const requestIds = []

const enrichedRequests = await Promise.all(
     requestIds.map(async (requestId) => {
       const request = await requestClient.fromRequestId(requestId);
       const requestData = request.getData();

       return {
         request: requestData,
         paymentSettings: {
           // In this case, we are paying the requests in USDC
           currency: {
             type: Types.RequestLogic.CURRENCY.ERC20,
             value: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
             network: "sepolia",
           },
         },
         paymentNetworkId:
           Types.Extension.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT,
       };
     })
);

In Batch Requests too, Request Network provides approval checking functions for ERC-20 tokens. Simply, import the function and use it.

import { approveErc20BatchConversionIfNeeded,
} from "@requestnetwork/payment-processor";

await approveErc20BatchConversionIfNeeded(
        enrichedRequests[0].request,
        signer._address,
        signer,
        undefined,
        {
          currency: {
            type: Types.RequestLogic.CURRENCY.ERC20,
            value: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
            network: "sepolia",
          },
          maxToSpend: "0",
        },
        "0.1.0"
);

If you have more than one ERC-20 token, you are paying for, use the following code.

import { approveErc20BatchConversionIfNeeded
} from "@requestnetwork/payment-processor";

for (const enrichedRequest of enrichedRequests) {
        await approveErc20BatchConversionIfNeeded(
          enrichedRequest.request,
          signer._address,
          signer,
          undefined,
          {
            currency: {
              type: Types.RequestLogic.CURRENCY.ERC20,
              value: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
              network: "sepolia",
            },
            maxToSpend: "0",
          },
          "0.1.0"
        );
      }

Finally, you can pay your batch request at once through payBatchConversionProxyRequest.

import { payBatchConversionProxyRequest
} from "@requestnetwork/payment-processor";

const tx = await payBatchConversionProxyRequest(
        enrichedRequests,
        signer,
        {
          skipFeeUSDLimit: true,
          conversion: {
            currencyManager: currencyManager,
          },
          version: "0.1.0",
        }
      );

await tx.wait(2);

Congratulations, you have now fulfilled the Batch ERC-20 Payment request. Additionally, you can wait to see if all the requests have been fulfilled.

import { RequestNetwork } from "@requestnetwork/request-client.js";

const requestClient = new RequestNetwork({
    nodeConnectionConfig: {
      baseURL: "https://sepolia.gateway.request.network/",
    },
});

// Multiple RequestIds for multiple requests
const requestIds = []

await Promise.all(
        requestIds.map(async (requestId) => {
          while (true) {
            const request = await requestClient.fromRequestId(requestId);
            const requestData = await request.refresh();

            if (requestData.balance?.balance >= requestData.expectedAmount) {
              await checkForCompletedTask(task);
              break;
            }

            await new Promise((resolve) => setTimeout(resolve, 1000));
          }
        })
      );

console.log("Batch Payment Successful!!");

Video gif. A little boy in a Chuck E. Cheese birthday crown dances in celebration. Text, “Celebrate!!!”

Conclusion

Remember, the key to successful implementation lies in thorough testing, robust error handling, and a deep understanding of both the technical mechanics and the broader blockchain ecosystem.

The journey into blockchain-powered payments is just beginning, and tools like Request Network are paving the way for more efficient, transparent, and accessible financial technologies.

Happy coding!