Get Started

Learn how to build a recommendation engine with Airstack APIs and integrate it into your React application.

Prerequisites

Step 1: Create a Query Using AI

For this tutorial, you will focus on building a contact recommendation based on addresses that have interacted with the user in token transfers. To get all the addresses, you will first need to get all the historical token transfers that occur on the user’s wallet.

In this example, suppose the user is 0xd8da6bf26964af9d7eed9e03e53415d37aa96045. Therefore, in the prompt, type the following text to get all the token transfers with 0xd8da6bf26964af9d7eed9e03e53415d37aa96045:

For 0xd8da6bf26964af9d7eed9e03e53415d37aa96045, get all token transfers

After clicking enter, the Airstack AI will output a GraphQL query and return responses as follows:

query GetTokenTransfers {
  Wallet(input: {identity: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", blockchain: ethereum}) {
    tokenTransfers {
      id
      chainId
      blockchain
      from {
        identity
        blockchain
      }
      to {
        identity
        blockchain
      }
      type
      tokenAddress
      operator {
        identity
        blockchain
      }
      amount
      formattedAmount
      tokenId
      amounts
      tokenIds
      tokenType
      transactionHash
      blockTimestamp
      blockNumber
      tokenNft {
        id
        address
        tokenId
        blockchain
        chainId
        type
        totalSupply
        tokenURI
        contentType
        contentValue {
          image {
            original
          }
        }
        metaData {
          name
          description
          image
          attributes {
            trait_type
            value
            displayType
            maxValue
          }
          imageData
          backgroundColor
          youtubeUrl
          externalUrl
          animationUrl
        }
        rawMetaData
        lastTransferHash
        lastTransferBlock
        lastTransferTimestamp
      }
      token {
        id
        address
        chainId
        blockchain
        name
        symbol
        type
        totalSupply
        decimals
        logo {
          small
          medium
          large
          original
          external
        }
        contractMetaDataURI
        contractMetaData {
          name
          description
          image
          externalLink
          sellerFeeBasisPoints
          feeRecipient
        }
        rawContractMetaData
        baseURI
        lastTransferTimestamp
        lastTransferBlock
        lastTransferHash
        projectDetails {
          collectionName
          description
          externalUrl
          twitterUrl
          discordUrl
          imageUrl
        }
        tokenTraits
      }
    }
  }
}

Use Airstack AI's Best Practices to get the best and most accurate result for your query.

These are a lot of data gained from single query, but not every field is needed. Thus, in the next step, you’ll modify the query to trim down some of the fields and only have the one necessary for building the contacts recommendation feature.

Step 2: Modify Your Query

For your purpose, the fields that are most important for the contacts recommendation feature are the from and to fields that will provide information on the sender and the receiver of the token transfers.

Therefore, you can remove all the unnecessary fields and reduce your query to as follows:

query GetTokenTransfers {
  Wallet(input: {identity: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", blockchain: ethereum}) {
    tokenTransfers {
      from {
        identity
      }
      to {
        identity
      }
    }
  }
}

With this query, you will only receive from.identity and to.identity which will provide the sender or receiver of the token transfers. From this response, either the sender or the receiver will have a different address to 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 that you can utilize and format as a list of contact recommendations for your user.

Despite getting the fields that you need, this query is only limited to only calling a single blockchain which is Ethereum. If there are token transfers occurring on other chains then your contact recommendation will be incomplete.

Instead of making a separate request to fetch the same information on another blockchain, in this case, take Polygon, you can use Airstack to construct a cross-chain query.

Thus, the final query will look as follows:

query GetTokenTransfers {
  ethereum: Wallet(input: {identity: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", blockchain: ethereum}) {
    tokenTransfers {
      from {
        identity
      }
      to {
        identity
      }
    }
  }
  polygon: Wallet(input: {identity: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", blockchain: polygon}) {
    tokenTransfers {
      from {
        identity
      }
      to {
        identity
      }
    }
  }
}

Step 3: Add Variables to Your Query

Adding variables to your constructed Airstack query is very simple. All you need is to simply define a parameter to your query and replace the input, in this case identity, with the variable.

For this example, let’s add an $address variable as follows:

query GetTokenTransfers($address: Identity!) {
  ethereum: Wallet(input: {identity: $address, blockchain: ethereum}) {
    tokenTransfers {
      from {
        identity
      }
      to {
        identity
      }
    }
  }
  polygon: Wallet(input: {identity: $address, blockchain: polygon}) {
    tokenTransfers {
      from {
        identity
      }
      to {
        identity
      }
    }
  }
}

Step 4: Create Your React Project (Optional)

First, create an empty React project if you don’t have any. Otherwise, you can skip this part and jump to getting your API key below.

First, use Vite to generate a new React project:

npm create vite@latest airstack-demo -- --template react

After the process is successfully completed, an airstack-demo folder containing the empty React project will be created. Now, you can enter the folder and download some dependencies:

cd airstack-demo && npm install

Step 5: Get Your API Key

First, login to your Airstack account and go to Settings. Under the Default Key, you can find and copy your API key.

Then, create a .env file if you don’t have any by running the following command:

touch .env

And, paste your API key to your environment variable .env file.

VITE_AIRSTACK_API_KEY=YOUR_API_KEY

Step 6: Integrate Airstack into Your React App

To use Airstack in your React project, install the Airstack React SDK.

npm install @airstack/airstack-react

Once the installation is complete, you should see that it has been added to your package.json as a dependency and is ready to be imported.

{
  "dependencies": {
    "@airstack/airstack-react": "^0.3.3"
    // other dependencies
  }
  // other config
}

Now that all the basic setups are complete, let’s go to src/App.jsx file and add Airstack to your React application.

First, let’s delete all the original template contents inside src/App.jsx and add new code for Airstack integration.

To use the Airstack React SDK, first, initialize the SDK by adding the following code in App.jsx.

import { init, useQuery } from "@airstack/airstack-react";

init(import.meta.env.VITE_AIRSTACK_API_KEY);

Then, add a basic component with useQuery hook added to call the query that you constructed in previous sections to build contact recommendations based on historical token transfers:

import { init, useQuery } from "@airstack/airstack-react";

init(import.meta.env.VITE_AIRSTACK_API_KEY);

function App() {
  const query = `
  query GetTokenTransfers($address: Identity!) {
    ethereum: Wallet(input: {identity: $address, blockchain: ethereum}) {
      tokenTransfers {
        from {
          identity
        }
        to {
          identity
        }
      }
    }
    polygon: Wallet(input: {identity: $address, blockchain: polygon}) {
      tokenTransfers {
        from {
          identity
        }
        to {
          identity
        }
      }
    }
  }  
  `;
  const variables = {
    address: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
  };

  const { data, loading, error } = useQuery(query, variables, { cache: false });

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return <>{JSON.stringify(data)}</>;
}

export default App;

With this simple code, you essentially just integrated Airstack APIs that fetch all the addresses that interacted with the 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 address in token transfers across both Ethereum and Polygon. The app will output the JSON result on the UI and will look like this:

At its current state, you have just successfully loaded Airstack and its data responses to the UI. However, it is a very bare minimum and the data is loaded still in its JSON stringified format.

As an improvement, let’s add an input box to provide the user address and some basic `div` elements to render the list of recommended contacts.

To do so, modify the components code as follows:

import { init, useLazyQuery } from "@airstack/airstack-react";
import { Fragment, useMemo, useState } from "react";

init(import.meta.env.VITE_AIRSTACK_API_KEY);

function App() {
  const query = `
  query GetTokenTransfers($address: Identity!) {
    ethereum: Wallet(input: {identity: $address, blockchain: ethereum}) {
      tokenTransfers {
        from {
          identity
        }
        to {
          identity
        }
      }
    }
    polygon: Wallet(input: {identity: $address, blockchain: polygon}) {
      tokenTransfers {
        from {
          identity
        }
        to {
          identity
        }
      }
    }
  }  
  `;

  const [fetchTokenTransfers, { data, loading }] = useLazyQuery(
    query,
    {},
    { cache: false }
  );
  const [identity, setIdentity] = useState("");

  const recommendationsArray = useMemo(() => {
    const { ethereum, polygon } = data || {};
    const { tokenTransfers: ethereumTokenTransfers = [] } = ethereum || {};
    const { tokenTransfers: polygonTokenTransfers = [] } = polygon || {};
    return [...ethereumTokenTransfers, ...polygonTokenTransfers]
      .map((transfer) => {
        if (
          transfer.from.identity !== identity &&
          transfer.from.identity !==
            "0x0000000000000000000000000000000000000000"
        ) {
          return transfer.from.identity;
        } else if (
          transfer.to.identity !== identity &&
          transfer.to.identity !== "0x0000000000000000000000000000000000000000"
        ) {
          return transfer.to.identity;
        } else {
          return;
        }
      })
      .filter(Boolean);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [data]);

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        justifyContent: "center",
        width: "100vw",
      }}
    >
      <form
        onSubmit={(e) => {
          e.preventDefault();
          fetchTokenTransfers({ address: identity });
        }}
      >
        <input
          value={identity}
          onChange={(e) => {
            e.preventDefault();
            setIdentity(e.target.value);
          }}
          placeholder="Enter Ethereum address"
          style={{
            width: "300px",
            height: "30px",
            borderRadius: "5px",
            padding: "5px",
          }}
        />
        <button type="submit">Resolve</button>
      </form>
      <h1>Recommendations</h1>
      <p>
        {loading ? (
          "Loading..."
        ) : (
          <Fragment>
            {recommendationsArray?.map((recommendation, key) => (
              <div
                key={key}
                style={{
                  margin: ".5rem",
                  padding: "1rem",
                  border: "1px solid white",
                }}
              >
                {recommendation}
              </div>
            ))}
          </Fragment>
        )}
      </p>
    </div>
  );
}

export default App;

The new UI looks as follows:

As you can see, aside from having a component that renders the addresses that can be recommended based on the input address, you also added several new aspects.

First, since the app needs to call the API manually instead of every first render, it is essential to replace useQuery with useLazyQuery, which provides you with an additional function that is named fetchTokenTransfers to call the API on each button clicked.

In order to make sure that the fetchTokenTransfers is called accordingly to the user input, the input value in the input element is toggled to the identity React state such that each time the value in the input box changes the state will be changed.

Thus, when the user clicks the button, fetchTokenTransfers will always call the Airstack API with the variables provided by the user input directly.

Once the fetchTokenTransfers is called and the data variable is changed to the latest response, the recommendations list will be generated and formatted based on the data variable. Here, recommendationsArray is going to have data as a dependency, and therefore the value will be updated when data is updated.

Thus, with the recommendationsArray value that contains the list of contact recommendations address, it can be rendered in the component wrapped in a simple div element. However, if you’re building for your application, feel free to modify the UI as you’d like.

Congratulations! 🎉You’ve just learned how to use Airstack to build a simple contact recommendation feature for and integrate the APIs into your React application.

Last updated

#189: add-poaps-use-cases

Change request updated