transfer()
event.transfer()
function directly affects the token balance of an account.
transfer()
function facilitates the transfer of tokens from one account to another. By mandate, the function must also emit a Transfer
event that carries the details of the accounts involved in the transfer and the value of the tokens that were transferred.
By capturing and processing the Transfer
events that were emitted, we could access all the above-mentioned transfer information plus the token’s address (contract). With all data, I say we could create a nice index of token balances. So, let’s get to it.
transfer()
function and the associated event are mandatory, meaning that every ERC-20 token contract would have to implement them, and they would do so in a uniform format.
The graph-cli uses the contract address to fetch the contract ABI, which is required for accessing the contract functions. Since every ERC-20 token contract will have the transfer()
function and the Transfer
event, we can use the ABI of any given ERC-20 token contract for accessing them.
You can get the token contract address from Etherscan. To avoid confusion, you can set the contract name as ERC20
, as a nod to the generic nature in which we will be using its ABI.
/abis
directory, the contract ABI is saved as ERC20.json
(based on the contract name that we provided).
schema.graphql
. Within this file, we will define the data objects or entities we need. When you analyze our use case, you can see that in order to access the token balance, we also need information regarding the account and the token involved. Based on this requirement, let’s model our schema file:
Token
and Account
entities, we have also modeled the token balance as a separate entity, TokenBalance
. Since the token balance is associated with an account, we have declared a balances
field inside the Account
entity and declared it as a list of token balances; [TokenBalance!]
. This represents the fact that a single account can have multiple token balances. The @derivedFrom
directive in the field is used to declare it as a reverse lookup. By doing so, we have created a virtual field on the Account
entity (Balances
) that is derived from the relationship defined on the TokenBalance
entity (account
). Thus, the Balances
field need not be set manually using the mapping file.
Now that we are done with the schema let’s work on the manifest file.
subgraph.yaml
in your code editor, you will see that it is populated with many details pertaining to the contract that we mentioned while setting up the project. For our project, we won’t be needing many of these details as most of them are specific to that contract. So, edit the file in the following way:
entities
with the ones that we have defined. We have also removed all the eventHandlers
except the one for the Transfer
event and in the source
section, we have removed the contract address, as our subgraph is not directed towards any particular address.
We have also added the startBlock
parameter in the source
section. This will specify the block from which we wish to start the indexing.
And with that, we have our new and improved manifest file.
/generated
directory:
eventHandlers
, mentioned in our manifest file. As with the manifest file, if you open the auto-generated mapping file erc-20.ts
inside the /src
directory, you will find out that it contains the code for various eventHandlers
, but we can remove most of them and work on the handleTransfer
event handler.
Now, before we start editing our core mapping file, I would like you to create a separate file in the /src
directory. Within this file, we will be defining the code for:
utils.ts
inside our /src
directory and add the following code:
fetchTokenDetails()
gets us the details of a token. It does so by accessing the token address from the event
parameter and binding it with the ERC20
(token contract ABI) class. This will let us access all the public, read-only functions from the token contract, and using those functions we can retrieve the details like token name, symbol, and decimals. The fetchAccount()
function fetches the details of the account.
The fetchBalance()
function retrieves the balance of a particular token in a given account. To fetch the balance, the function uses the token address, which is given as a function parameter, and binds it with the ERC20
class. This allows you to access the balanceOf()
function, which takes the account address as the parameter and returns the token balance. The balanceOf()
is a read-only function that is implemented as part of the ERC-20 token standard.
In the code, we also use the graph-ts library to import certain useful data types. This library is automatically installed when we set up the project.
Once you add the new code, your project structure should look like this:
/src/erc-20.ts
.
Transfer
eventhandleTransfer
event handler. The code should be defined as a function of the same name, handleTransfer()
:
handleTransfer()
function takes the Transfer
event as the parameter. From the event, the code retrieves the address of the token and the addresses of accounts involved in the transfer. These addresses are used to fetch the details of the token and the accounts respectively. For retrieving the details, we make use of the functions that we defined in the utils.ts
file. Once we have all the information, we use it to set the token balances of both accounts involved in the transfer.
With that, we have everything that we need to deploy our subgraph, so let’s build our code and deploy the subgraph using Chainstack.
.wasm
format. The build outputs will be stored inside the /build
directory.
Now, let’s deploy the subgraph.
first
— specifies the maximum number of results to return.skip
— specifies the number of results to skip.orderBy
and orderDirection
parameters.
orderBy
— specifies the field by which to sort the results.orderDirection
— specifies the direction of the sort, either asc
for ascending or desc
for descending.