This post will introduce you to several topics, It summarises my exploration process and serves as a brief introduction to fetching data from the Ethereum blockchain. The entire project is available for download via a link at the bottom of this post.

I had not previously spent any time with Ethereum other than purchasing a few NFTs on OpenSea. My only previous experience with blockchain networks is with Solana. I took part in a mini weekend hackathon: Build a Web3 app on Solana with React and Rust. That was pretty fun, I learned how to run a local Solana validator and interact with a Phantom wallet. I deployed a few images to the Solana Devnet. If you are interested in learning web3 technology, you should check out the free courses on offer at buildspace.so.

My goal is to make some sort of data visualization for Braintrust, the BTRST token or their DAO (decentralized autonomous organization), plus I want to learn more about web3 and blockchain technology in general.

Without knowing what data is publicly available, I can't visualize that data, so I needed to start exploring what data is stored on the blockchain regarding Braintrust. I will do a quick introduction to Braintrust and then share a small app that I created, you will learn how to start extracting and displaying data from the Ethereum blockchain.

What is the Braintrust network?

Braintrust* is the world's first user-owned and controlled talent network. The Braintrust mission is to "build the world's most impactful talent network — one that is user-owned, aligns incentives, and redistributes value to Talent and Organizations".

The network has some of the best talent and clients in the world, big brands such as Intel, IBM, Nasa, Nike, Nestlé and Porsche (to name a few), partner with pre-vetted talent to create long-lasting relationships, and it seems to be working well. The clients pays a very modest 10% fee and the talent gets to keep the entire agreed hourly rate.

At the time of writing, the average job size on the network was quoted at $72,296, not a small sum of money for a freelance gig, and that represents one of the impressive numbers that comes out of the incredible story which is unfolding.

What is the BTRST token?

Braintrust uses blockchain technology to transparently distribute control of its network to the community members who contribute to building it. The Braintrust token (BTRST) is an ERC-20 token deployed on the Ethereum blockchain. There is a maximum supply of 250 million tokens.

The token is also used as an incentive to reward the community for building and growing the decentralized Braintrust network. You can earn BTRST tokens by inviting and vetting talent or referring clients to grow the network. The token is used as a payment mechanism, sent to talent that literally help build and add additional code to the network via Braintrust grants.

Ownership and governance in the Braintrust network is represented by the BTRST token.

What is the Braintrust fee converter?

This is where it gets interesting. I mentioned earlier how clients pay lower fees. Well, a percentage of that fee is sent to the Braintrust fee converter. The fee converter is a smart contract on the Ethereum blockchain that accepts USDC and buys back BTRST tokens from the open market. These tokens are returned to the BTRST treasury. The tokens from the treasury are used to pay talent to build the network via grants, creating a flywheel that continually provides funding for future projects within the Braintrust network.

What is the Braintrust DAO?

The collective vision for the future of Braintrust is voted on by the community. The Braintrust DAO (decentralized autonomous organization) is the collective community of Braintrust users who decide the direction of the network.

With the fee converter in place, Braintrust now has a mechanism for creating revenue and will be faced with deciding what to do with that revenue. Who better to decide than the community that use and build Braintrust? Interestingly the fee converter was proposed by, voted on, and implemented by the community in October 2021.

Users who build the network will be rewarded with tokens and those tokens are used as voting rights. You can follow along with how the network changes are being introduced via Snapshot, owning BTRST tokens allows users to vote on upcoming proposals.

Fetching data from the Ethereum blockchain

So that was a brief introduction to Braintrust. Let's get back to the article's main topic, fetching and displaying data from the Ethereum blockchain.

Web3.js vs ethers.js

A few Google searches led me to two libraries that can be used to pull data from the Ethereum blockchain. web3.js and ethers.js. I came across another very interesting project called moralis, but that will be a topic for another post.

"ethers.js is best for those who prefer lightweight, user-friendly libraries with robust testing and documentation."

The above quote found here, sealed the deal. ethers.js is the newer of the two libraries and seems to be growing in popularity, plus I wanted to move quickly so lightweight + good documentation sounded perfect.

Basic project set-up

After deciding to use the ethers.js library, I set up a small React project and started to explore the data on the blockchain. Below I outline some of the process with more details, I also included the entire code at the bottom of this post for reference and a download link for the complete project.

My basic approach was the following:

  1. Create a React app with Typescript
  2. Install ethers
  3. Setup a simple state reducer for rapid prototyping and exploration
  4. Discover details about deployed contracts and the BTRST token
  5. Start exploring Ethereum, ethers and Braintrust documentation
  6. Start to query the blockchain
  7. Display the retrieved data on screen

I spent some time exploring etherscan and the Braintrust website to locate the contract and token addresses. I include them here as they are used later to fetch data.

A simple state reducer

I made a simple state reducer to allow for quick prototyping. This reducer allowed me to quickly add/remove state key names while fetching and understanding the response data.

type State = {
  someRandomKeyName: string;
};

type Action = Record<string, any>;

const initialState: State = {
  someRandomKeyName: 'a value',
};

function reducer(state: State, action: Action) {
  switch (action.type) {
    case 'update':
      return {
        ...state,
        ...action.payload,
      };
    default:
      throw new Error();
  }
}

To use the reducer I do the following:

const [state, dispatch] = useReducer(reducer, initialState);

dispatch({
  type: 'update',
  payload: { someRandomKeyName: 'a value' },
});

Now with a simple state reducer, it is possible to start exploring more of the ethers documentation.

Connecting to the Ethereum blockchain

First, create a provider and tell it which network to connect to. If you do not pass a network name, it will connect to the default network known as Mainnet.

// const provider = ethers.getDefaultProvider("ropsten"); // Testnet
const provider = ethers.getDefaultProvider(); // Mainnet

Constructing a Human-Readable ABI

When a developer deploys a contract to the Ethereum network, they can create functions on that contract, and the names of those functions will be decided by the developer who deployed the contract. To interact with the contract, you will need to know these names and their expected parameters.

If you visit the deployed Braintrust contract and scroll down, you will find the contract ABI (Application Binary Interface). Here you can discover the function names, inputs/outputs and data types of the deployed contract.

With this knowledge, you can construct a Human-Readable ABI to interact with a contract.

For the purpose of this post, I interact with a few read-only functions of the Braintrust contract.

// Construct an ABI
const abi = [
  // Read-Only Functions
  'function balanceOf(address owner) view returns (uint256)',
  'function totalSupply() view returns (uint256)',
  'function symbol() view returns (string)',
];

Create a new Contract instance

Now I have a provider, the Braintrust contract address and an ABI, I can create a new Contract instance and pass it the required parameters.

// Create Contract instance and pass it the contract address, ABI and provider
const erc20 = new ethers.Contract(state.tokenAddress, abi, provider);

Retrieving data from the blockchain

Now I have a contract instance I can call one of the previously defined methods from the ABI. I take the response from the network and store it in my React reducer state. The code below grabs the number of tokens currently held by the Braintrust DAO.

At the time of writing, the DAO has 222,957 BTRST tokens at its disposal, which translates to roughly $760'283.37 @ $3.41 per token.

erc20.balanceOf(state.daoContractAddress).then((btrstBalance: BigNumberish) => {
  const balance = formatUnits(btrstBalance).split('.')[0];
  const formatBalance = commify(balance);

  dispatch({
    type: 'update',
    payload: { daoBalance: formatBalance },
  });
});

BigNumberish is a type from the ethers library. The response from the Ethereum blockchain can be outside the range of safe values possible in JavaScript.

The btrstBalance response must be parsed with ethers.utils.formatUnits() to convert it to a string. I drop the numbers after the decimal point and use another helper ethers.utils.commify() to add commas to the number of tokens.

That's a Wrap!

I spent around 4 hours messing around and learned quite a bit in that time. I hope this tutorial can be a bridge to working with blockchains. If you are interested in working with some of the world's best companies, you can use my referral link to join the Braintrust* network. As always, if you have any questions or feedback, please leave me a comment below and I would be happy to reply.

Complete code example

The following code is from a small React app which renders a couple of sentences populated with data from the Ethereum blockchain. The project is available for download via my GitHub.

BTRST supply and DAO tokens

import React, { useEffect, useReducer } from 'react';
import './App.css';
import { ethers } from 'ethers';
import type { BigNumberish } from 'ethers';

type State = {
  contractAddress: string;
  tokenAddress: string;
  daoContractAddress: string;
  daoBalance: string;
  tokenName: string;
  tokenSupply: string;
};

type Action = Record<string, any>;

const initialState: State = {
  contractAddress: '0x799ebfabe77a6e34311eeee9825190b9ece32824', // Braintrust Contract
  tokenAddress: '0x799ebfabe77a6e34311eeee9825190b9ece32824', // BTRST Token
  daoContractAddress: '0xb6f1F016175588a049fDA12491cF3686De33990B', // Braintrust DAO Contract
  daoBalance: '',
  tokenName: '',
  tokenSupply: '',
};

function reducer(state: State, action: Action) {
  switch (action.type) {
    case 'update':
      return {
        ...state,
        ...action.payload,
      };
    default:
      throw new Error();
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  useEffect(() => {
    // const provider = ethers.getDefaultProvider("ropsten"); // Test Net
    const provider = ethers.getDefaultProvider(); // Main Net
    const { commify, formatUnits } = ethers.utils;

    const abi = [
      // Read-Only Functions
      'function balanceOf(address owner) view returns (uint256)',
      'function totalSupply() view returns (uint256)',
      'function symbol() view returns (string)',
    ];

    const erc20 = new ethers.Contract(state.tokenAddress, abi, provider);

    erc20.symbol().then((tokenName: string) => {
      dispatch({
        type: 'update',
        payload: { tokenName: tokenName },
      });
    });

    erc20.totalSupply().then((tokenSupply: BigNumberish) => {
      const balance = formatUnits(tokenSupply).split('.')[0];
      const formatBalance = commify(balance);

      dispatch({
        type: 'update',
        payload: { tokenSupply: formatBalance },
      });
    });

    erc20
      .balanceOf(state.daoContractAddress)
      .then((btrstBalance: BigNumberish) => {
        const balance = formatUnits(btrstBalance).split('.')[0];
        const formatBalance = commify(balance);

        dispatch({
          type: 'update',
          payload: { daoBalance: formatBalance },
        });
      });
  }, [state.tokenAddress, state.daoContractAddress]);

  return (
    <div className="App">
      <p>
        Total token supply: {state.tokenSupply} {state.tokenName}
      </p>
      <p>
        DAO Balance: {state.daoBalance} {state.tokenName}
      </p>
    </div>
  );
}

export default App;

* Links on this page marked with an asterisk are referral links to the Braintrust web3 network. If you get accepted to the network at no additional cost to you, a commission might be paid to me in BTRST tokens. These tokens can be used as voting rights for voting on change proposals to the network.