Interact with the smart contract details

All right! So you've deployed you smart contract through the SCUI! Lets show the end-user the contract details and if possible, interact with the contract as well.

So, if you clicked on the finish button after the last deployment step, you would have been redirected to this screen:

contract/0x1234.../fixed-supply/show

The contract/:contractAddress/show/:contractType page.

This page displays the contract details that were setup in the contract/:contractType/create page. Easy, right?

Let's have a look at what this component looks like.

Create the fixed-supply component inside the contract-show module

ng generate component contract-show/fixed-supply --project=simple-ico

The component must be generated by the angular cli and you should be ready to get your contract details.

Display the fixed-supply component with the matching :contractType param

https://github.com/SimpleICO/web-app/blob/master/src/app/contract-show/container.component.ts
...
import { FixedSupplyDeployment } from '@factory/fixed-supply.deployment';

@Component({
  selector: 'app-container',
  templateUrl: './container.component.html',
  styleUrls: ['./container.component.css']
})

export class ContainerComponent implements OnInit {

  contractAddress: string

  contractType: string

  FixedSupplyDeployment: string = FixedSupplyDeployment._type
https://github.com/SimpleICO/web-app/blob/master/src/app/contract-show/container.component.html
<app-header></app-header>
<app-mobile-menu></app-mobile-menu>

<main id="crowdsale-show">

  <app-fixed-supply *ngIf="FixedSupplyDeployment == contractType"></app-fixed-supply>

</main>

Get the fixed-supply contract details

By now, you should be familiar with the frontend framework, so here's the whole component code.

Basically, what is happening here is:

  1. Init the component with ngOnInit()

  2. Get the contractAddress from the route params

  3. Initialize a new SimpleCrowdsaleContract instance passing the Wallet instance

  4. Connect to the contract ABI and set the contractAddress from the route

    1. This is equivalent to passing the address to the new web3.eth.Contract call or calling new Contract().at() in other libraries

  5. Subscribe to the crowdsale contract events. These events are the transactions happening for this contract in the Ethereum network. Web3 listens to these events using websockets

  6. Instantiate a new SimpleTokenContract passing the Wallet instance and connect to its ABI

  7. Get the crowdsale data. Note that we are delegating these methods to the SimpleCrowdsaleContract class. This is a good design practice

  8. Get the token data. Because the crowdsale contract has the token address stored in it, we can get it by calling the this.crowdsale.instance.methods.token.call() method. Await for it to return the value and set the address to the token contract instance

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { EthereumService } from '@service/ethereum.service';
import { WalletService } from '@service/wallet.service';
import { SharedService } from '@service/shared.service';
import { SimpleCrowdsaleContract } from '@contract/simplecrowdsale.contract';
import { SimpleTokenContract } from '@contract/simpletoken.contract';

declare var require: any

const ethers = require('ethers')

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

  contractAddress: string

  contractType: string

  ethRaised: string = '0.0'

  crowdsale: SimpleCrowdsaleContract

  token: SimpleTokenContract

  txHistory: Array<any> = []

  txs: Array<string> = []

  constructor(
    public route: ActivatedRoute,
    public eth: EthereumService,
    public shared: SharedService,
    public wallet: WalletService) {}

  ngOnInit() {
    this.route.params.subscribe(({ contractAddress, contractType }) => {
      this.contractAddress = contractAddress
      this.contractType = contractType

      this.crowdsale = new SimpleCrowdsaleContract(this.wallet.getInstance())
      this.crowdsale.connect()
      this.crowdsale.setAddress(this.contractAddress)
      this.subscribe()

      this.token = new SimpleTokenContract(this.wallet.getInstance())
      this.token.connect()

      this.getCrowdsaleData()
      this.getTokenData()
    })
  }

  refresh(){
    this.crowdsale.getEthRaised()
    this.crowdsale.getAvailableTokens(this.token)
  }

  subscribe(){
    this.crowdsale.subscribeToEvents()
      .on('data', event => {
        this.crowdsale.getEthRaised()
        this.crowdsale.getAvailableTokens(this.token)

        this.getTransaction(event.transactionHash)
      }).on('error', error => {
        console.log(error)
      })
  }

  async getTransaction(hash: string){
    if (this.txs.indexOf(hash) != -1) {
      return false
    }

    this.txs.push(hash)

    try {
      let tx = await this.crowdsale.web3.eth.getTransaction(hash)

      this.txHistory.unshift({
        hash: tx.hash,
        from: tx.from,
        to: tx.to,
        value: `ETH ${ethers.utils.formatEther(tx.value)}`
      })
    } catch (error) {
      console.log(error)
    }
  }

  async getCrowdsaleData(){
    this.crowdsale.getEthRaised()
    this.crowdsale.getBeneficiary()
    this.crowdsale.getPrice()

    this.crowdsale.web3.eth.getPastLogs({
      fromBlock: '0x0',
      address: this.crowdsale.address
    }).then(res => {
      res.forEach(rec => {
        this.getTransaction(rec.transactionHash)
      })
    }).catch(err => console.log)
  }

  async getTokenData(){
    let tokenAddress = await this.crowdsale.instance.methods.token().call()
    this.token.setAddress(tokenAddress)

    this.crowdsale.getAvailableTokens(this.token)

    this.token.getName()
    this.token.getSymbol()
  }
}

Whenever a transaction happens for this crowdsale smart contract in the Ethereum address, the subscription will populate the txHistory array and display it in the SCUI in real-time.

This is how the SCUI html looks like. Note that we are binding to the crowdsale and token instances. This is a good design practice. Also the template will show the data whenever it is ready or it changes:

<app-qr-code-modal [model]="crowdsale"></app-qr-code-modal>

<div class="container">
  <h1 class="token-name">{{ token.name }} ({{ token.symbol }})</h1>
  <div class="eth-raised">
    <span class="balance text-truncate" style="max-width: 210px">{{ crowdsale.address }}</span>
    <small>Crowdsale address</small>
    <span class="go-icon" (click)="shared.displayCrowdsaleShowModal()"><i class="icon-copy"></i></span>
  </div>
  <div class="eth-raised">
    <span class="balance"><strong>ETH</strong> {{ crowdsale.ethRaised }}</span>
    <small>Amount raised</small>
    <!-- <span class="go-icon" (click)="refresh()"><i class="icon-sync"></i></span> -->
  </div>
  <div class="eth-raised">
    <span class="balance"><strong>ETH</strong> {{ crowdsale.price }}</span>
    <small>Price</small>
  </div>
  <div class="eth-raised">
    <span class="balance"><strong>{{ token.symbol }}</strong> {{ crowdsale.tokens }}</span>
    <small>Available</small>
  </div>
</div>

<section id="info" class="section">
  <div class="container">
    <h5 class="title">Contract information</h5>
    <article class="info">
      <small>Crowdsale beneficiary</small>
      <a href="{{ eth.etherscanURL }}/address/{{ crowdsale.beneficiary }}" target="_blank" class="text-truncate">{{ crowdsale.beneficiary }}</a>
    </article>
    <article class="info">
      <small>Crowdsale contract</small>
      <a href="{{ eth.etherscanURL }}/address/{{ crowdsale.getAddress() }}" target="_blank" class="text-truncate">{{ crowdsale.getAddress() }}</a>
    </article>
    <article class="info">
      <small>ERC20 Token contract</small>
      <a href="{{ eth.etherscanURL }}/token/{{ token.getAddress() }}" target="_blank" class="text-truncate">{{ token.getAddress() }}</a>
    </article>
  </div>
</section>

<section id="tx-history" class="section">
  <div class="container">
    <h5 class="title">Transaction History</h5>
    <article class="tx row" *ngFor="let tx of txHistory">
      <div class="col-sm-3 hash">
        <small>Hash</small>
        <a href="{{ eth.etherscanURL }}/tx/{{ tx.hash }}" target="_blank" class="text-truncate">{{ tx.hash }}</a>
      </div>
      <div class="col-sm-3 from">
        <small>From</small>
        <a href="{{ eth.etherscanURL }}/address/{{ tx.from }}" target="_blank" class="text-truncate">{{ tx.from }}</a>
      </div>
      <div class="col-sm-3 to">
        <small>To</small>
        <a href="{{ eth.etherscanURL }}/address/{{ tx.to }}" target="_blank" class="text-truncate">{{ tx.to }}</a>
      </div>
      <div class="col-sm-3 value">
        <small>Value</small>
        <span class="text-truncate">{{ tx.value }}</span>
      </div>
    </article>
  </div>
</section>

Last updated