Deploying and accesing an Ethereum contract using ethers and Angular
Tags: ethereum, ethers, angular, smartcontract
Published: 2023-10-15 18:02:01

In the two previous posts of this serie, we have learned how to build a house-swap ethereum smart-contract using solidity and how to test it using hardhat. 
In this post, we will create an [angular](https://angular.io/) application and we will install [ethers](https://docs.ethers.org/v6/) to interact with the smart-contract and [ethers-typechain](https://github.com/dethcrypto/TypeChain/tree/master/packages/target-ethers-v6) to generate typescript types from our contract json abi.
> The contract json abi is generated after compiling the contract using hardhat as we did in the previous post.
> In the last post, we also used typechain to generate contract types since we needed them to build the tests.

## Before starting

This article assumes that the reader has a basic knowledge about the angular framework. This means that the reader knows basic framework concepts like components, services and the angular injector. In the same way, the article assumes that the reader knows basic concepts about cryptocurrencies like what custodial and non custodial wallets are.
I will try to push the angular app code to my github account as soon as possible.

## Creating an ethereum account

We will need to get an ethereum address and fund it with some *GoerliETH*. Goerli is an ethereum testnet. We are going to deploy our contract to this network.

To create an ethereum account, we are going to install [metamask](https://metamask.io/). Metamask is an easy-to-use non custodial wallet which we can install as an extension in our browser. 
> Use this kind of wallets only for testing purposes or for holding few assets. 

Go to [metamask site](https://metamask.io/) and click on the *Download* button. In the next page, you will see a button for installing metamask as an extension of your browser. Follow the instructions to install and creating your account.

### Selecting the test network

After having metamask installed as a browser extension, we have to select the *Goerli* testnet to ensure we are working on a test network. To select it, click on the top left corner button and you will see a networks list. Select the *Show test networks* checkbox and then select Goerli network.

### Funding the account with GoerliETH 

After having created the account and selected Goerli, we will see that we hold 0 *GoerliETH*. We need some *GoerliETH* to test our smart-contract. The fastest way to get them is to use a faucet. 
An ethereum faucet is a tool to get test ETH. In our case, we are going to use the [goerli faucet](https://goerlifaucet.com/). Go to the Goerli faucet link and you will se the following page:


![Goerli faucet page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/daaobkozs6npneutyxq7.png)

Before requesting *GoerliETH* you will have to follow the next steps:

- **Creating an account on [Alchemy](https://www.alchemy.com/)**. Alchemy is a web3 platform that allows developers to create and deploy decentralized applications and also provides tools, sdk's and api's to scale them. Enter into [alchemy](https://www.alchemy.com/), click on *Sign up* button and follow the steps to create your account.
- **Funding your account on the mainnet**: In order to prevent abuse, goerli faucet requires accounts to have a minimum of 0.001 ETH on the mainnet (actually 1,55$). To fund your account with real ETH you will have to follow the next steps:
   - Go to metamask, switch to mainnet and copy your address.
   - Go to an exchange (coinmotion, coinbase, binance or the the one of your choice), buy some ETH and send them to your metamask address.

After receiving ETH on your address, you are ready to get some *GoerliETH*. Go back to the goerli faucet and click on *signup or login with Alchemy*. After being logged, paste your address on the input and click on *Send me ETH*. If everything works, you will see a new transaction and after a few seconds you should have received your *GoerliETH* in your metamask account.
Now, we are ready to start our [angular](https://angular.io/) app.

## Creating the angular application

We are going to use [angular-cli](https://angular.io/cli) to create a new angular application so, if you have not installed it yet, install it using [npm](https://www.npmjs.com/). 

```
npm install -g @angular/cli
```
Now that we have angular-cli installed, let's create a new angular application using it:

```
ng new <the_app_name_of_your_choice>
```
Open the project folder with an editor of your choice (I am using [vscode](https://code.visualstudio.com/)) and you will see the folders and files created by the angular-cli command which are required to start developing. 
Before starting to write code, let's install ethers and typechain since we will need them to complete the rest of the article.

```
npm install ethers @typechain/ethers-v6
```

### Creating the services

We are going to create three services each of them will be responsible for an specific task:

#### The WalletService

The wallet service will be in charge of prompting metamask so we can select the account we want to use to deploy the contract. 
Let's create the wallet service using angular-cli:

```
ng g service service/walletService
```
This will create an *app/service* folder and will create the service into it. Let's see now the code of the service:

```typescript
import { Injectable } from '@angular/core';
import { BrowserProvider, Signer } from 'ethers';

@Injectable({
  providedIn: 'root'
})
export class WalletService {

  constructor() { }

  getSigner(): Promise<Signer> {
    const provider = new BrowserProvider(window.ethereum);
    return provider.getSigner();
  }
}
```
The *getSigner* method contains two lines, the first one opens the metamask prompt using *BrowserProvider*. The *BrowserProvider* allows us to access a wallet installed on the browser. The next line, gets the signer from the provider. We need the account signer to deploy and interact with the contract. 
> Notice that, the function does not return the signer itself but a Promise. The caller will have to resolve it and save the signer.

#### The ContractDeployerService

This service will be in charge of getting the contract abi and deploying the contract. 

```
ng g service service/contractDeployerService
```

Let's see the code:

```typescript
import { Injectable } from '@angular/core';
import { BaseContract, ContractFactory, ContractRunner } from 'ethers';
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ContractDeployerService {

  private contractUrl: string = '/assets/house_swap.json';

  constructor(private httpClient: HttpClient) { }

  getContractFromSolidity(solidityOutput: any, signer: ContractRunner): ContractFactory {

    return ContractFactory.fromSolidity(solidityOutput, signer);
  }

  deployContract(contract: ContractFactory): Promise<BaseContract>{
    return contract.deploy();
  }

  getContractAbi(): Observable<any> {
    return this.httpClient.get(this.contractUrl);
  }
}
```
The *getContractFromSolidity* method gets an ethers *ContractFactory* object which we will need later to deploy the contract. The *solidityOutput* parameter is the json generated after compiling the solidity contract as we did in the previous post using hardhat. 
> We used the *npx hardhat compile* command to compile the solidity contract. This command generated an *artifacts* folder which contained that json file. In this post, we have to copy that file and paste it in our angular project *assets* folder.

The *deployContract* method uses the *ContractFactory* *deploy* method to deploy the contract. Notice again that the *deployContract* method returns a promise so the caller will have to resolve it.

The *getContractAbi* method uses angular [http client](https://angular.io/api/common/http/HttpClient) to get the contract abi json from the assets folder url.

#### The ContractInteractorService

This service is responsible of interacting with the contract, that is, calling the contract functions passing the required parameters. We can generate as we did for the last ones.

```typescript
import { Injectable } from '@angular/core';
import { ContractTransactionResponse, Signer } from 'ethers';
import { House_swap__factory, House_swap } from '../types';
import { HouseSwap } from '../types/House_swap';

@Injectable({
  providedIn: 'root'
})
export class ContractInteractorService {

  contract!: House_swap;

  constructor() {}

  load(contractAddress: string, signer: Signer): void {
    this.contract = House_swap__factory.connect(contractAddress, signer);
  }

  initialize(houseType: string, link: string, value: number, address: string): Promise<ContractTransactionResponse> {
    const house: HouseSwap.HouseStruct = {
      houseType: houseType,
      value: value,
      link: link,
      propietary: address
    };

    return this.contract.initialize(house);
  }
}

```
You can notice here that there are two imports (the last ones) which import objects from a *types* folder. This objects are contract mappings generated with typechain which allow us to call contract methods as if they were class methods.
To generate types folder, you will have to execute this command in your project folder:

```
./node_modules/.bin/typechain --target ethers-v6 src/assets/*.json --out-dir ./src/app/types
```
Let's see now the service methods.

The *load* method gets the contract deployed address and the signer and creates a *House_Swap* contract using *House_swap__factory* *connect* method. After connecting it, every call to the contract will be made by the signer passed as parameter (that must be the signer provided by metamask prompt).

The *initialize* method creates a *HouseStruct* object (which is a map for the solidity contract *House* struct). Remember that the contract *initialize* method requires a *House* struct as a parameter. Then it calls the contract *initialize* method passing the created *HouseStruct* object as a parameter. The method returns a promise which will hold a *ContractTransactionResponse* after resolving.

### Creating the component to interact with the services

So far, we have created the services which interact with the service and the contract. In this section we will create an [angular component](https://angular.io/api/core/Component) which will interact with the services for getting the signer, deploying the contract and interacting with it.

To create the component, use angular-cli:

```
ng g component component/home
```
This will create a *component* folder and the component files into it. Let's see now the component code:

```typescript
import { Component, OnInit } from '@angular/core';
import { BaseContract, Signer, ContractTransactionResponse } from 'ethers';
import { lastValueFrom } from 'rxjs';
import { ContractDeployerService } from 'src/app/service/contract-deployer.service';
import { ContractInteractorService } from 'src/app/service/contract-interactor.service';
import { WalletService } from 'src/app/service/wallet.service';
import { HouseSwap } from 'src/app/types/House_swap';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  protected signer!: Signer;

  constructor(private walletSrv: WalletService, private contractDeployerSrv: ContractDeployerService, private contractInteractorSrv: ContractInteractorService){

  }

  ngOnInit(): void {
  }


  connectWallet() {
    this.walletSrv.getSigner().then(
      (signer: Signer) => {
        this.signer = signer;
        console.log(this.signer);
      }
    )
  }

  async deployContract() {

    const contractAbi$ = this.contractDeployerSrv.getContractAbi();
    const contractAbi  = await lastValueFrom(contractAbi$);

    const abiRaw: string = JSON.stringify(contractAbi);
    const contractFactory = this.contractDeployerSrv.getContractFromSolidity(abiRaw, this.signer);

    this
      .contractDeployerSrv
      .deployContract(contractFactory)
      .then(
        (contract: BaseContract) => {
            contract.waitForDeployment().then(
              async () => {
                const contractAddress = await contract.getAddress();
                this.contractInteractorSrv.load(contractAddress, this.signer);
                console.log(contractAddress);
              }
            ).catch(
              (error: any) => {
                console.log(error);
              }
            );
        }
      )
      .catch(
        (error: any) => {
          console.log(error);
        }
    )

  }

  async initializeContract() {
    const address = await this.signer.getAddress();
    const house: HouseSwap.HouseStruct = {
      houseType: 'apartment',
      link: 'https://www.infolink.com',
      value: 52665,
      propietary: address
    }
    this
      .contractInteractorSrv
      .initialize(house)
      .then(
        async (callResult: ContractTransactionResponse) => {
          console.log(callResult.toJSON())
      })
  }

}

```

As we can see, the component constructor injects the three services from the last section. Let's see each method one by one:

```typescript
connectWallet() {
    this.walletSrv.getSigner().then(
      (signer: Signer) => {
        this.signer = signer;
      }
    )
  }
```

The *connectWallet* method uses *walletService* *getSigner* method to promt metamask and gets the signer. After resolving the promise, the signer is saved on component *signer* variable.

```typescript
async deployContract() {

    const contractAbi$ = this.contractDeployerSrv.getContractAbi();
    const contractAbi  = await lastValueFrom(contractAbi$);

    const abiRaw: string = JSON.stringify(contractAbi);
    const contractFactory = this.contractDeployerSrv.getContractFromSolidity(abiRaw, this.signer);

    this
      .contractDeployerSrv
      .deployContract(contractFactory)
      .then(
        (contract: BaseContract) => {
            contract.waitForDeployment().then(
              async () => {
                const contractAddress = await contract.getAddress();
                this.contractInteractorSrv.load(contractAddress, this.signer);
              }
            ).catch(
              (error: any) => {
                console.log(error);
              }
            );
        }
      )
      .catch(
        (error: any) => {
          console.log(error);
        }
    )

  }
```

The *deployContract* method is a little more extensive. Let's explain it step by step:

- The first line uses *contractDeployerService* to get an observable. The next line uses [RxJS lastValueFrom](https://rxjs.dev/api/index/function/lastValueFrom) to get the contract json data.

- The next two lines generate an string json from the contract json abi and uses *contractDeployerService* *getContractFromSolidity* to generate an ethers *ContractFactory* object.

- Then, we use *contractDeployerService* *deployContract* method to deploy the contract to *Goerli*. The *deployContract* method returns a promise which (after resolving) returns an ethers *BaseContract* object. We also have to use *BaseContract* *waitForDeployment* method to ensure contract has been fully deployed. Then, we get the contract address and use *contractInteractorService* *load* method to create the *House_Swap* object to be ready to interact with the contract. If there are errors during the process, we show them in the console.

```typescript
async initializeContract() {
   const address = await this.signer.getAddress();
   const house: HouseSwap.HouseStruct = {
      houseType: 'apartment',
      link: 'https://www.infolink.com',
      value: 52665,
      propietary: address
   }
   this
     .contractInteractorSrv
     .initialize(house)
     .then(
        async (callResult: ContractTransactionResponse) => {
          console.log(callResult.toJSON())
   })
}
```

The *initializeContract* uses *contractInteractorService* *initialize* to invoke contract *initialize* method. The service method returns a promise which, after resolving, holds an ethers *ContractTransactionResponse* object. It we log it, we will see many parameters. The next image shows some of them:


![Transaction Result JSON](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpz3hxyxdwzlhyckxxfi.png)


In the image, we can see three parameters, the gas limit, the gas price (transaction fee in wei) and the hash. Now, we can copy the hash a go to the [goerli etherscan](https://goerli.etherscan.io/) and search for the transaction information using the hash. 


![Goerli Etherscan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/85thzm4nknw29dwlqvk1.png)

As we can see in the image, the transaction has been resolved successfully which means we've been invoked *initialize* method without errors.

## Conclusion

In this third post, we have seen how to create an account in metamask and how to get some Goerli ETH from the goerli faucet. Then, we have learned how to install ethers and typechain within an angular application and use them to deploy and invoke our contract.
In order not to extend this article too much, I am going to create one last article for this serie which will show how to call the other contract functions and follow the contract flow.