🧬

Angular,cosmos-clientによるブロックチェーンWebアプリフロントエンド開発3(Staking機能)

2022/03/28に公開

株式会社 CauchyE の角田です。

今回は前回に引き続き、Angular/ 弊社製 cosmos-client-ts で実装した Web アプリケーションに機能を追加していきます。

今回は Staking 機能を追加し、ローカル環境で Cosmos-SDK 製ブロックチェーンを立ち上げて動作を確認します。

また、コードの分量が多くなってきたので、機能ごとにコンポーネントを分割することも併せて行います。

実行環境

  • OS
    • Ubuntu 20.04 (on WSL2)
  • Node.js
    • v14.19.20
  • npm
    • 8.5.1
  • Starport
    • v0.19.3

Staking, Validator, Delegatorについて

Stakingとは

ステーキングとは、指定されたトークンをロックすることで、ブロックチェーンのネットワークに参加し、対価として報酬が貰える仕組みです。

Validatorとは

ノードの運営や新しいブロック承認、ブロックチェーンに対する提案の決議など、実際の管理作業を行うアカウントです。作業と引き換えに、報酬やトランザクションの手数料一部を受け取ることができます。
このようにバリデーターは自身でノードを維持管理している必要があります。長期間の停止など、適切にノードの維持ができていないとペナルティを受けステーキングしたトークンを失う可能性もあります。

Validatorになるには

バリデーターになるには、ノードを運営しステーキングを行う必要があります。その上で専用のトランザクションを発行しバリデーターになることを宣言します。立候補したアカウントのうちでステーキング量が多い上位 150 のアカウントがバリデーターに任命されます。

Delegatorとは

バリデーターになる条件を満たすことはできないが、ネットワーク管理に携わりたいアカウントは、Delegation(委任)をすることでそれが可能になります。自身の保有するトークンを、とあるバリデーターに託してステーキングすることで、バリデーターの報酬の一部を受け取ることができます。

以上の説明を図にまとめます。

Validator と Delegator について、CosmosHub(公式ドキュメント)に FAQ がありました。併せて参考にしてみてください。Validator FAQ

ローカルチェーンのステーキング状況

前々回立ち上げた Cosmos-SDK 製ブロックチェーンは、Bob と Alice の 2 アカウントが存在していて、Alice が 100,000,000stake をステーキングしています。これは starport で scafforld コマンドにて作成したフォルダ mars の直下にある config.yml で定義されています。
これがチェーンでのステーキングの初期値になります。

mars/config.yml
accounts:
  - name: alice
    coins: ["20000token", "200000000stake"]
  - name: bob
    coins: ["10000token", "100000000stake"]
validator:
  name: alice
  staked: "100000000stake"

コンポーネント分割

前回前々回、作成したリポジトリに機能を追加していきますが、1 つのファイルにまとめるには少々大きくなってきたので、機能ごとにコンポーネントを分割します。

コンポーネント整理

  • app.component.ts --> 前回前々回変更したコンポーネント。ここから機能を分割します。

  • alice.component.ts --> アカウント Alice の情報を common ディレクトリの子コンポーネントに受け渡します。

  • bob.component.ts --> アカウント Bob の情報を common ディレクトリの子コンポーネントに受け渡します。

├── app.component.html
├── app.component.ts // ここから各機能を切り出します。
├── alice
│   ├── alice.component.html
│   └── alice.component.ts 
├── bob
│   ├── bob.component.html
│   └── bob.component.ts
└── common
    ├── address
    │   ├── address.component.html
    │   └── address.component.ts // <--アカウントのアドレスを表示する機能
    ├── balances
    │   ├── balances.component.html
    │   └── balances.component.ts // <--アカウントの残高を表示する機能
    ├── delegations
    │   ├── delegations.component.html
    │   └── delegations.component.ts // <--アカウントのステーキング情報を表示する機能
    ├── send
    │   ├── send.component.html
    │   └── send.component.ts // <--トークンを送信する機能 ※今回実装
    └── staking-delegate
        ├── staking-delegate.component.html
        └── staking-delegate.component.ts // <--Validatorに委任する機能 ※今回実装

delegations.component, staking-delegate.component は今回実装します。

コンポーネント分割の例(Typescript)

分割の一例として balances.component の例を示します。
app.component.ts より残高の取得に関する部分を切り出します。

balances.component.ts
import { Component, OnInit, Input } from '@angular/core';
   ()

@Component({
  selector: 'app-balances',
  templateUrl: './balances.component.html',
})
export class BalancesComponent implements OnInit {
  @Input()
  address?: AccAddress | null;

  @Input()
  sdk?: cosmosclient.CosmosSDK | null;

  balances$: Observable<InlineResponse20028Balances[] | undefined>;
  timer$: Observable<number> = timer(0, 3 * 1000);

  constructor() {
    this.balances$ = this.timer$.pipe(
      mergeMap((t) => {
        if (
          this.sdk === undefined || this.sdk === null ||
          this.address === undefined || this.address === null
        ) {
          return [];
        } else {
          return rest.bank
            .allBalances(this.sdk, this.address)
            .then((res) => res.data.balances);
        }
      })
    );
  }
  ngOnInit(): void {}
}

親コンポーネントから、SDK(システム情報)とアドレスを受け取って、残高を取得して表示しています。3 秒に一度更新します。シンプルですね。

コンポーネント分割の例(html)

balances.component.html
<ng-container *ngIf="balances$ | async as balances">
  <div class="inline-block py-1 px-2 rounded bg-green-50 text-green-500 text-xs font-medium tracking-widest">Balances
  </div>
  <table class="table-auto">
    <thead>
      <tr>
        <th class="px-4 py-2">Token</th>
        <th class="px-4 py-2">Balance</th>
      </tr>
    </thead>
    <tbody>
      <ng-container *ngFor="let balance of balances">
        <tr>
          <td class="border px-4 py-2"> {{ balance.denom }}</td>
          <td class="border px-4 py-2"> {{ balance.amount }}</td>
        </tr>
      </ng-container>
    </tbody>
  </table>
</ng-container>

機能的には balances.component.ts で更新する balances の Observable を async パイプで受け取って表示しています。
表示面では tailwiind を使用して、レイアウトを修正しています。

このようにして、前回作成した、app.component.ts、app.component.html を分割していきます。

分割の結果は、以下 github に置いています。
https://github.com/taro04/cosmos-client-ts-sample-code

Angular のコンポーネント分割の詳細は参考記事が多くあるので詳細は記載しませんが、必要に応じて以下の記事を参照ください。

tailwind による画面デザインについては、必要に応じて下記参照ください。

cosmos-client-tsによる機能追加

今回は以下2つの機能を追加します。

1,Staking確認機能

あるアカウントが、どれくらいのステーキングをしているかを表示します。
具体的には、どのバリデーターに、どれくらいの量をステーキングしているかを確認します。
cosmos-client-ts の delegatorDelegations 関数で実装します。

delegations.component.ts

    this.delegations$ = this.timer$.pipe(
      mergeMap((t) => {
        if (
          this.sdk === undefined || this.sdk === null ||
          this.address === undefined || this.address === null
        ) {
          return [];
        } else {
          return rest.staking
            .delegatorDelegations(this.sdk, this.address)
            .then((res) => res.data.delegation_responses);
        }
      })
    );
  }

balances とほぼ同様です。関数に確認したいアカウントのアドレスを入力しています。

返り値がオブジェクトで、必要な情報(委任先のバリデーターとステーキング量)が以下に示す階層にある点は注意が必要かもしれません。

{
  "balance": {
    "denom": "stake",
    "amount": "100000000"
  },
  "delegation": {
    "delegator_address": "cosmos1z976umhuawv34gc8v7uq6rzy509w09vrt36a7h",
    "validator_address": "cosmosvaloper1z976umhuawv34gc8v7uq6rzy509w09vrw9wgjy",
    "shares": "100000000.000000000000000000"
  }
}

2,Staking設定機能

数量を指定してアカウントからバリデーターにトークンを送信し、ステーキングを行います。
基本的な以下の流れは前回のメッセージ送信と変わりません。注意すべきはトランザクションのボディが MsgSend ではなく、MsgDelegate である点です。

amount は委任する量を設定します。量は任意に変えることができますが、トークンの種類はネットワークで指定されたものが必要です。このネットワークでは'stake'である必要があります。

delegations.component.ts
    // build tx
    const msgDelegate = new proto.cosmos.staking.v1beta1.MsgDelegate({
      delegator_address: fromAddress.toString(),
      validator_address: validatorAddress,
      amount, // {denom:'stake',amount:'????'}
    });

MsgSend と同じように、委任元(デリゲーター)、委任先(バリデーター)、数量を設定します。

実装

上記の 2 つの機能を含めた実装は、以下の github を参照ください。
https://github.com/taro04/cosmos-client-ts-sample-code

動作確認

数量を入力して、Stake のボタンを押すと、Bob が Alice にステーキングを行います。

<実行前>
Before staking

<実行後>
After staking

  • Alice の Delegation 欄の validator に自身の validator アドレスが表示されていることを確認してください。
  • Alice の Delegation の token が config.yml で設定した、100,000,000 であることを確認してください。
  • Bob の Delegation 欄の validator に Alice の validator アドレスが表示されていることを確認してください。
  • ボタンを押すごとに Bob の Delegation の token が増えていくことを確認してみてください。

まとめ

1 つにまとめていたコンポーネントを機能ごとに分割しました。機能追加として、ステーキングの確認と設定機能を実装し、動作確認を行いました。Cosmos-SDK 製チェーンにおけるステーキングの概要と、前回実装したメッセージ送信機能を基本として少々改造することで機能の追加ができてしまう簡単さが伝われば幸いです。

CauchyE は一緒に働いてくれる人を待ってます!

ブロックチェーンやデータサイエンスに興味のあるエンジニアを積極的に採用中です!
以下のページから応募お待ちしております。
https://cauchye.com/company/recruit

Discussion