Reading ethereum contract events using Angular and ethers

icolomina

Nacho Colomina Torregrosa

Posted on November 2, 2023

Reading ethereum contract events using Angular and ethers

In this last post of this serie, we are going to see how to query contract events using ethers. To do it, we are going to use the contract deployed in the previous post. There, we deployed a contract to Goerli test network and called initialize function. Now, we are going to call addOffer function and then use ethers to query the NewOffer event emitted by the addOffer function.

The code

You can find a minimal code here. It includes an angular application with:

  • The services to connect the wallet, deploy the contract and interact with it.
  • The HomeComponent with the methods to connect, deploy and interact with the contract
  • A minimal HTML with a button which connects the wallet.

The aim of this code is not to serve as a ready project but as a template so developers can use it as a base for building any dapp using angular.

I've done some changes in the wallet connect process since the button is located in the app component and it has to communicate to the home component when the wallet is connected.

If you want to run the project, you will have to follow the next steps before:

  • Compile the contract as we did in the second post using hardhat. Then copy the file located in artifacts/contracts/HouseSwap.sol/HouseSwap.json to the angular project assets folder with the name house_swap.json.
  • Generate the mapping type using typechain. Refer to the previous post to check how to do it. Ensure to execute the command in the src/app folder so the mappings being generated under src/app/types folder.

Then, you can run the ng serve command.

Adding the new function to ContractInteractorService

In the last post, we created a service called ContractInteractorService which was in charge of interacting with the contract. Let's add a method to call addOffer function

addOffer(house: HouseSwap.HouseStruct, amountPayOriginToTarget: number, amountPayTargetToOrigin: number): Promise<ContractTransactionResponse> {
    return this.contract.addOffer(house, amountPayOriginToTarget, amountPayTargetToOrigin);
}
Enter fullscreen mode Exit fullscreen mode

As we can see, the function is simple, it uses the contract instance to call addOffer passing the required params.

Remember that, as we used typechain to map contract types to typescript, we can call the contract function as it was a class method.

Adding the function to the HomeComponent

Let's go now to the HomeComponent to add the method which will call the ContractInteractorService addOffer function.

async addOffer(contractAddress: string) {
    const address = await this.signer.getAddress();
    const house: HouseSwap.HouseStruct = {
      houseType: 'apartment',
      link: 'https://www.infolink2.com',
      value: 44158,
      propietary: address
    }

    this
      .contractInteractorSrv
      .addOfer(house, 0, 199865245)
      .then(
        async (callResult: ContractTransactionResponse) => {
          console.log(callResult.toJSON())
    })
}
Enter fullscreen mode Exit fullscreen mode

This function is similar to the initializeContract function. The only difference is that, in this case, we are calling to addOffer function.

addOffer function result

The image above shows some data of the call result. As we did in the previous post, we can copy the transaction hash, go to the goerli ethersan and query the transaction information.

addOffer transaction information

As we can see in the image, the transaction has been successfully executed.

Quering the contract NewOffer events

As the addOffer function emits a NewOffer event, we need to query contract NewOffer events so the owner contract could read them.
Let's use queryFilter ethers function to achieve this.
First of all, we will add a new method in the ContractInteractorService:

queryFilters(): Promise<Array<TypedEventLog<any>>> {
    return this.contract.queryFilter(this.contract.filters.NewOffer);
}
Enter fullscreen mode Exit fullscreen mode

This method is pretty simple, it uses contract queryFilter method to retrieve NewOffer emitted events.

Both contract queryFilter and filters object are result of the typechain mapping.

Now, let's go back to the HomeComponent and add a new method called getContractOffers.

getContractOffers(contractAddress: string) {
    this.contractInteractorSrv.queryFilters().then(
      (result: TypedEventLog<any>[]) => {
        if(result.length > 0) {
          result.forEach((e: TypedEventLog<any>) => {
            const eventData = e.args[0][0];
            console.log('HouseType:' + eventData[0]);
            console.log('value:' + eventData[1]);
            console.log('Link:' + eventData[2]);
            console.log('Propietary:' + eventData[3]);
          })
        }
      }
    );
}
Enter fullscreen mode Exit fullscreen mode

The getContractOffers uses the recently added ContractInteractorService queryFilters function to retrieve the events. If there are events, it loops each of them and logs their args.

addOffer NewOffer data

The above image shows the result of the getContractOffers method logs. This would be the way as the contract owners should read the offers.

Resume

In this last post we have seen, specifically, how to read contract events using ethers. That is the last aspect regarding the interaction with smart contracts.
Quering contract events is important since it allows users to respond to such events. In this case, the contract owner can respond to a NewOffer event if he would want to accept it.

💖 💪 🙅 🚩
icolomina
Nacho Colomina Torregrosa

Posted on November 2, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related