DEV Community

Eray Kaya
Eray Kaya

Posted on

Type-Safe Querying of Ethereum Contract Data with React-Query and TypeChain

Indeed, moving forward from our previous discussion on how to generate types from smart contract abi, we can now dive deeper into transforming these type-safe functions into type-safe queries and mutations using react-query. In this segment, I'll be focusing on the process of reading contract data with react-query queries.

First and foremost, let's understand that to interact with a smart contract, a provider is necessary. This could be a public provider, a private provider utilizing API keys from Alchemy or Infura, or simply a user's wallet provider. For the purpose of this post, we will use a public RPC provider. However, for live projects, I would strongly recommend opting for Alchemy or Infura providers.

Moreover, it's essential to consider that depending solely on a user's wallet provider for reading data is not an ideal strategy. The primary reason being that users need to be connected to view the data. Secondly, the provider they're using remains uncertain. Hence, we aim to implement a type-safe data fetching method from the contract as smoothly as an ordinary API.

Let's take an example to better understand this process - Fetching Ethereum USD price from Chainlink's Price Converter Contract. After we generate our types with typechain, let's start to write our read hook.

First we start with getting the contract instance, for the sake of focusing using react-query for reading smart contract I am writing all these functions not generic so you can convert it to generic if you need.

const getContractInstance = () => {

// address of chainlink contract: https://etherscan.io/address/0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419#readContract

  const address = "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419";
// public provider to read data, I recomment to use either Alchemy or Infura provider for production: 
  const provider = new JsonRpcProvider("https://eth.llamarpc.com");
  const contract = EthUsdFeedContract__factory.connect(address, provider);
  return contract;
};
Enter fullscreen mode Exit fullscreen mode

Now let's write our fetch(read) function:

export const getPriceConversion = async () => {
  const contract = getContractInstance();
  const price = await contract.latestAnswer();
  const decimals = await contract.decimals();
  const priceInEtherUnit = formatUnits(price, decimals);
  return priceInEtherUnit;
};
Enter fullscreen mode Exit fullscreen mode

Finally we can create our typesafe hook:

type EthToUsd = string;

export const useEthUsdFeed = <TData = EthToUsd, TError = unknown>(
  options?: UseQueryOptions<EthToUsd, TError, TData>
) => {
  return useQuery<EthToUsd, TError, TData>(
    ["cryptoToUsd"],
    async () => await getPriceConversion(),
    options
  );
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)