BLOG — Tutorials

31 days ago

How to use Web3 Functions to build serverless web3 apps

Use off-chain computation power to supercharge your web3 smart contracts!

We call our smart contracts 'smart' because they know exactly how to do what we ask them to. Having immutable code that executes as expected is a great starting point, but the problem is that they don’t know when to execute - and timing is often all important when dealing with financial systems.

Since its inception Gelato has offered web3 users the ability to execute tasks based on on-chain conditions. This was already powerful; enabling developers to automate and relay transactions. Now Gelato is going one step further, offering web3 users the ability to run serverless functions in a decentralized environment. These functions can communicate both on-chain and off-chain, run any custom logic and send transactions on-chain.

Check out this blog post to learn more about Web3 Functions!

Decentralized Serverless Functions

You can think of web3 Functions as decentralized cloud functions that work similarly to AWS Lambda or Google Cloud Functions - but for web3. They enable developers to execute on-chain transactions based on arbitrary off-chain data (e.g. APIs,subgraphs, etc) and computation. These functions are written in Typescript, stored on IPFS and run by Gelato. For further reading you can check the docs.

Creating Web3 Functions

To accomplish our goal of creating a serverless, decentralized Web3 Function we will split our work into two stages:

  1. Writing, testing and deploying the function itself
  2. Creating the task that will execute the Web3 Function call

Writing, Testing and Deploying a Web3 Function

Follow these simple steps to get your Web3 Function up and running.

Clone our Web3 Functions template repo:

git clone https://github.com/gelatodigital/web3-functions-template

Cd into the directory and install dependencies:

cd web3-functions-template
yarn

Copy .env.example file to .env file and add the PROVIDER_URL, the RPC to use in your project for local testing. The private key is only required if you want to create the task programmatically.

When deployed your Web3 Function will use Gelato's RPCs - the PROVIDER_URL is just for your local testing

cp .env.example .env

Edit your .env file:

PROVIDER_URL="" # your provider URL (e.g. https://eth-mainnet.alchemyapi.io/v2/YOUR_ALCHEMY_ID)
PRIVATE_KEY="" # optional: only needed if you wish to create a task from the CLI instead of the UI

Writing a Web3 Function

We can now start writing our function in Typescript. Let's look at the ìndex.ts :

import {
  Web3Function,
  Web3FunctionContext,
} from "@gelatonetwork/web3-functions-sdk";
// Fill this out with your Web3 Function logic
Web3Function.onRun(async (context: Web3FunctionContext) => {
  const { userArgs, gelatoArgs, provider } = context;
  // Return execution call data
  return {
    canExec: true,
    callData: "YOUR_EXECUTION_PAYLOAD",
  };
});

We will add our custom logic within Web3Functions.onRun() . We can check on-chain contracts, off-chain data and query subgraphs. Once we determine whether the conditions are met to execute the transaction, we can return the calldata to be passed onchain. In the case that the conditions are not met we return:

  return {
    canExec: false,
    message: "Condition not met",
  };

A very basic example would be to ask off-chain for a random number API between 0 and 100 and to check on-chain whether our smart contract is active or not. If the number is greater than 75 and the active contract call returns true, we will execute a transaction in our contract.

Web3Function.onRun(async (context: Web3FunctionContext) => {
  const { userArgs, gelatoArgs, provider } = context;

  let abi = [
  "function active() view returns (bool)",
  "function setMockString(string) external",
]
  ];
  let contractAddress = "0x67C982310a687e43bA2A659b1117f6c5B73bB662";

  let contract = new ethers.Contract(contractAddress, abi, provider);
  let active = await contract.active();

  if (!active) {
    return { canExec: false, message: "Contract not active" };
  }

  let randomApi = `http://www.randomnumberapi.com/api/v1.0/random?min=0&max=100&count=1`;

  // we are using the lightweight ky library for doing the get request
  let randomApiRes: any = await ky.get(randomApi).json();

  if (!randomApiRes) throw Error("Get random number api failed");
  let random = randomApiRes[0] as number;

  if (random <= 75) {
    // We tell the executor that no transaction has to be executer
    return { canExec: false, message: "Number <= 75" };
  } else {
    let rString = randomString(10)
    const {data} = await contract.populateTransaction.setMockString(rString);

    return { canExec: true, callData: data! };
  }
});

This is a super basic example that barely scratches the surface of Web3 Function's capabilities!

Testing

The Web3 Functions templates install the gelatonetwork/web3-functions-sdk that exposes the test method for our Web3 Function:

npx w3f test src/web3Functions/display-string/index.ts

Deploy to IPFS

Once the Web3 Function is tested and our test logs show what we expect, we must deploy the Web3 Function to IPFS using the command:

npx w3f deploy src/web3Functions/display-string/index.ts

On completion of the deployment to IPFS we will receive the CID of our code that we will need later when creating our task.

Creating the task to run the Web3 Function

So we have our Web3 Function deployed ready to be executed. Now we will create the task that will query the code on IPFS, execute the code and - if the conditions are met - execute the target contract.

At the time of writing this blog, the Web3 Functions service is still in private beta, so only whitelisted addresses can create tasks. Please reach out to us to get whitelisted.

We create our task here.

Testnet transactions are subsidized by Gelato for the duration of the private Beta.

  1. Once your address is whitelisted we will head to this link to create the task:

  2. We will input the CID, the user arguments, the network the target smart contract is on, the smart contract address and the method to run:

  1. We can see the code of our Web3 Function by clicking on Check source code

  2. We name our task and we are good to go… Gelato will do the hard work for us!

Demo App

We have created a demo app to help you quickly familiarise yourself with the Web3 Functions development and deployment process.

Our demo app integrates the three technologies we need for our project:

  • Web3 Functions folder with our Web3 Functions code
  • A hardhat folder with our contracts and our deploy scripts
  • React frontend

This demo includes a contract that allows users to change a string variable when the active variable is true.

It also includes a serverless Web3 Function (the example shown above) that checks whether the active variable on the contract is true, calls an external API to fetch a random number between 0 and 100, and in the case that this number is greater than 75, creates a random string and executes the transaction to change it.

For demo purposes and to have a better dev experience we have included a basic react app where you can query the contract and update the active variable.

You can find the repo here. Please follow the readme.

The deployed app is available on https://web3-functions-demo.gelato.network/

We can see the task logs within the Web3 Functions applcaion:

Examples In the cloned template there is an examples folder with some additional examples of Web3 Functions. We hope you enjoy it, and feel free to reach out to us on Discord if you have any questions!