Openzeppelin Smart Contract Solidity ブロックチェーン

Openzeppelin Defenderでコントラクトを監視して、関数が実行されたらSlackに通知してみよう!

かるでね

こんにちは!CryptoGamesというブロックチェーンゲーム企業でエンジニアをしているかるでねです!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

このブログ以外でも情報発信しているので、よければ他の記事も見ていってください。

今回の記事では「Openzeppelin Defender」というサービスを使用して、「コントラクト内の特定の関数が実行されたときに、その情報をSlackに通知する機能」を実装していきます。

一般的にスマートコントラクトは公開されて誰にでも見れる状態にあります。

誰でも実行することはできてしまうため、怪しいアドレスから特定の関数が一度でも実行されたときにその情報を通知することで事前に怪しいアドレスの動きを封じることができるかもしれません。

コントラクト内の特定の関数が実行されたときに、その情報をSlackに通知する機能」をこの記事で実装して、ぜひさまざまな場面で使用していってください。

Openzeppelin Defenderとは?

そもそも「Openzeppelin Defender」とはどういったサービスなのでしょうか?

上記の公式ページによると、「スマートコントラクトのためのセキュアな運用プラットフォーム」と書かれています。

機能としては、大きく以下の4つのコンポーネントに分かれています。

機能

  • Admin
  • Relay
  • AutoTasks
  • Sentinels

では1つずつ詳細を見ていきましょう。

Admin

スマートコントラクトの管理を全て自動化し、安全性を確保する機能です。

安全なマルチシグコントラクトやタイムロックを通じてスマートコントラクトを管理するためのインターフェースとして機能します。

Defender Admin自体はコントラクトに対して全くコントロールを持ちません。

使用場面としては、以下が挙げられます。

使用場面

  • コントラクトをアップグレーダブルにする。
  • プロトコルの動作に影響を与えるような数値パラメータを調整する。
  • コントラクト内のメソッドを実行するためのアクセスコントロールリストを管理する。
  • 緊急時にコントラクトを一時停止する。

Relay

通常のHTTP APIを介してトランザクションを送信することができ、秘密鍵の安全な保管、トランザクションへの署名、ノンスの管理、ガス代の予測、トランザクションの再送信などを行うことができます。

バックエンドのスクリプトで秘密鍵を守ったり、トランザクションを監視してマイニングされる心配もありません。

使用場面としては、以下が挙げられます。

使用場面

  • コントラクトに状態遷移を起こす。
  • オンチェーンオラクルを外部データによって更新する。
  • ガスフリー体験をすることができる、メタトランザクションの実装。
  • 新規ユーザーにトークンをエアドロップすることで、アプリ内のサインアップに対応する。
  • プロトコルコントラクトからセキュアウォレットに資金を送金する。
  • スマートコントラクトのプロトコルと対話するボットの構築。

AutoTasks

スマートコントラクトを呼び出す自動化スクリプトを作成する機能です。

定期的にWebhooksを介して、もしくはトランザクションに応答してコードスニペットを実行することができます。

トランザクションの送信コントラクトからの発行されるイベントをもとに、定期的なアクションを自動化することができます。

使用場面としては、以下が挙げられます。

使用場面

  • コントラクトの残高を確認し、閾値に達した時点でウォレットに資金を送る。
  • 外部APIからの情報をもとにオンチェーンオラクルを更新する。
  • コントラクト内のデータを確認しオフチェーンデータソースが同期していることを確認する。
  • 条件を満たした時に、コントラクト内のデータを更新する。
  • Webhooksによる外部サードパーティサービスとの連携。
  • メタトランザクションにより、Dappユーザーにガスレストランザクションを提供する。

Sentinel

スマートコントラクトを監視して通知を送信する機能です。

2つのSentinelを用意しています。

Contract Sentinels

イベント、関数、トランザクションパラメータに条件を定義し、コントラクトに対するトランザクションを監視することができます。

Forta Sentinels

コントラクトアドレス、アラートID、重要度に関する条件を定義することでForta Alartで監視します。

定義した条件に基づいて、トランザクションまたはForta Alertに一致した場合、メールやSlack、Telegram、Discord、AutoTasksなどで通知することができます。

使用場面としては、以下が挙げられます。

使用場面

  • 所有権の移転、一時停止、アップグレードなど重要な機能の監視。
  • コントラクトに書かれた危険な処理に対する注意喚起。
  • 重要なイベントが発生した時にロジックを実行する。
  • メールやSlack、Telegram、DiscordまたはAutoTasksのカスタムインテグレーションを通じて、既存のWeb2ツールと統合する。
  • 想定外の取引量やアラートが発生したことを把握。

ここまで簡単に「Openzeppelin Defender」にまとめてきました。

次章から早速手を動かしていきましょう!

Openzeppelin Defenderに登録

まずは「Openzeppelin Defender」に会員登録してきましょう。

上記にアクセスして、「Sign up for free」をクリックしてください。

以下のページに遷移するので、「Sign up」をクリックしてください。

メールアドレスとパスワードを入力して、「Sign up」を押してください。

メールアドレスに確認メールが来るので、メールを開いて「Verify Email」というリンクをクリックしてください。

以下が表示されていれば登録完了です。

再度先ほど開いていた画面に戻り、「Sign in」をしてください。

以下のように名前を入力して、利用規約に同意するためのチェックを入れて、「Start using Defender」というボタンを押してください。

以下のような画面が表示されていれば登録完了です。

Slack登録

では次にSlackの登録と通知に必要な処理をしていきましょう。

上記からSlackアプリをダウンロードできます。

インストール手順については上記リンクを参考にしてください。

ワークスペースを作成する必要があるため、上記リンクを参考に作成してください。

次に先ほど作成したワークスペース内に、上記リンクを参考にチャンネルを新規作成してください。

次にWebhook URLを取得します。

上記リンクをクリックしてください。

開いたら以下のような画面が出てくるので、「Create an app」をクリックしてください。

そうするとページ遷移して以下のようなモーダルが出てくるので、「From scratch」をクリックしてください。

以下のようなモーダルに遷移するため、アプリ名、使用するワークスペース(先ほど作成したもの)を選択し、「Create App」をクリックしてください。

左のサイドメニューから「Incoming Webhooks」をクリックすると以下のようなページが開きます。

Activate Incoming Webhooks」の横にあるボタンをクリックして「On」にしてください。

その後、「Add New Webhook to Workspace」をクリックしてください。

以下の画面が開くので、通知したいチャンネルを指定して「Allow」をクリックしてください。

先ほどの画面に遷移して、下の方にWebhook URLが追加されているのが確認できます。

このURLは後ほど使用するためどこかにコピペしておくなどして後ほど確認できるようにしておいてください。

ここまでSlackにて必要な設定を行ってきました。

コントラクトの作成

では次にコントラクトを作成していきましょう。

今回はECR20トークンコントラクトを作成して、その中の各機能を監視して実行されたらSlackに通知していきます。

ではまず以下のGithub内のコードをローカルに持ってきてください。

ローカルに持って来れたら「README.md」に書かれているように以下のコマンドをターミナルで実行してください。

$ npm install
$ npx hardhat compile

実行できたら、コントラクトをGoerliテストネットのデプロイする以下のコマンドを実行してください。

$ npm run deploy:goerli

成功すると以下のように出力されます。

> erc20@1.0.0 deploy:goerli
> npx hardhat run scripts/deploy.ts --network goerli

ERC20 contract deploy address 0xAF66Bbf36f7ec78eB37984Df03562accDa58602b
Deploy completed

0xAF66Bbf36f7ec78eB37984Df03562accDa58602b」の部分がコントラクトアドレスになります。

上記のコントラクトアドレスは後ほど使用するので、どこかに控えておいてください。

以下のGoerli Etherscanを開いてコントラクトアドレスで検索をかけて表示されていればデプロイ完了です。

この章では基本的なコントラクトの設定をしてきました。

次章から本題の「Openzeppelin Defender」の設定をしていきます。

Openzeppelin Defenderで通知設定

ではこの章でコントラクトを監視して、特定のメソッドが実行されたときにSlackで通知するための「Openzeppelin Defender」の設定をしていきましょう。

まずは以下にアクセスしてください。

Relayの作成

最初に「Relay」の作成をしていきます。

サイドメニューから「Relay」をクリックして、「Add first Relay」をクリックしてください。

以下のような画面が出てくるので、「Name」に任意の名前を入力し、「Network」の部分で「Goerli」を選択して、「Create」をクリックしてください。

以下のような画面になっていれば「Relay」の作成は完了です!

Relay」が作成できたら以下の記事などを参考に、0.05 ~ 0.1goerli ETHをRelayアドレスへ送金してください。

Relay」のアドレスは先ほどのRelayerの画像を拡大した以下の赤枠部分です。

これで「Relay」の作成は完了です。

Autotaskの作成

では次に「Autotask」を作成していきましょう。

サイドメニューから「Autotask」をクリックして、「Add first Autotask」をクリックしてください。

以下の画面が出てくるので、「Name」に任意の名前を入力し、「Trigger」は「Webhook」を選択してください。

「Connect to a relayer (optional)」では、先ほど作成したRelayerを選択してください。

少し下にスクロールすると以下の「Code」という部分が出てきます。

Code」の部分には以下のコードを貼り付けて、「Create」ボタンをクリックしてください。

20行目では前章でデプロイしたコントラクトのアドレスを入力してください。

const { Relayer } = require("defender-relay-client");
const { ethers } = require("ethers");
const { RelayerParams } = require("defender-relay-client/lib/relayer");
const { DefenderRelaySigner, DefenderRelayProvider } = require('defender-relay-client/lib/ethers');

exports.handler = async function(credentials) {
  const abi = [
      {
        inputs: [],
        name: "pause",
        outputs: [],
        stateMutability: "nonpayable",
        type: "function",
      }
    ];
    const relayer = new Relayer(credentials);
    const provider = new DefenderRelayProvider(credentials);
    const signer = new DefenderRelaySigner(credentials, provider, { speed: 'fast' });

    const contractAddress = "デプロイしたコントラクトアドレス";

    const contract = new ethers.Contract(contractAddress, abi, signer);
    const pauseTx = await contract.pause({ gasLimit: 300000 });
    await pauseTx.wait();
    console.log(pauseTx);
    return pauseTx.hash;
  }

これで「Autotask」の作成は完了です!

Sentinelの作成

サイドメニューから「Sentinel」をクリックして、「Add first Sentinel」をクリックしてください。

以下のページが開くので、「Sentinel Type」は「Connect」を選択し、「Sentinel name」には任意の名前を入力してください。

Network」は「Goerli」を選択してください。

少し下にスクロールすると、「Addresses」という項目が出てくるので、先ほどデプロイしたコントラクトアドレスを入力してください。

コントラクトアドレスを入力すると以下のように「ABI」を書き込む箇所が出てきます。

この「ABI」に書き込む内容は先ほど作成したコントラクト内の、「ERC20/artifacts/contracts/Token.sol/Token.json」ファイル内の「"abi"」という項目の値です。

わからない方は以下のGithub内のコードをそのままコピペしてください。

必要な情報を入力したら「Next」をクリックしてください。

そうすると下の方に入力欄が追加されます。

Transaction properties」の箇所で検知するトランザクションの条件を指定できます。

以下のように設定すると失敗したトランザクションのみ検知することができます。

成功したトランザクションのみ取得したい場合は「status=='success'」と入力してください。

Transaction properties」の下にある「Contract conditions」の「Event」は、「Paused」、「RoleGranted」、「RoleRevoked」、「Unpaused」にチェックを入れてください。

Contract conditions」の「FUNCTIONS」は、「burn」、「grantRole」、「pause」、「revokeRole」、「unpause」にチェックを入れてください。

上記にチェックを入れることができたら、「Next」をクリックしてください。

以下の画面が表示されるので、「Create new notification」をクリックしてください。

クリックしたのち出てくるメニューから「Slack Webhook」をクリックしてください。

そうすると以下のようなモーダルが出てきます。

Alias」には任意の識別子を入力して、「Webhook URL」には全前章で作成したSlackのWebhook URLを入力してください。

上記2つを入力できたら「Send Test Message」をクリックしてください。

そうすると通知したいチャンネルにテストメッセージが届くので、もし届いていたら問題なく接続ができています。

最後に「Save」をクリックしてください。

先ほどの画面に戻るので、作成した「Notification channels」にチェックを入れて、「Create Sentinel」をクリックしてください。

これで「Openzeppelin Defender」の設定は完了です。

変更の手順も機能の追加をしながらご紹介していきます。

サイドメニューから「Sentinel」をクリックして、先ほど作成した「Sentinel」をクリックしてください。

以下の画像のようなページが表示されるので、右上の歯車マークをクリックして「Edit Notifications」をクリックしてください。

以下のような画面が表示されるので、「EXECUTE AN AUTOTASK」の部分で先ほど作成した「Autotask」を選択してください。

選択できたら「Save changes」をクリックしてください。

これで変更は完了です

次章では実際に通知できるかテストしていきます!

Slackへ通知できるかテスト

では最後に実際にSlackに通知できるかテストしていきましょう!

まず、.envファイルを作成していきましょう。

.env.sampleファイルがあるディレクトリに移動して、ターミナルで以下のコマンドを実行してください。

$ cp .env.sample .env

.envファイルでは環境変数を定義しているため、以下の情報を追記してください。

環境変数

  • INFURA_KEY
  • ETHERSCAN_API_KEY
  • PRIVATE_KEY
    • ウォレットに秘密鍵。
  • OTHER_ADDRESS_PRIVATE_KEY
    • PRIVATE_KEYで設定したウォレット以外の秘密鍵。
  • PUBLIC_KEY
    • PRIVATE_KEYで設定した秘密鍵から生成されたウォレットアドレス。
  • OTHER_ADDRESS
    • OTHER_ADDRESS_PRIVATE_KEYで設定した秘密鍵から生成されたウォレットアドレス。
  • RELAY_ADDRESS
    • Openzeppelin Defenderで設定したRelayerのアドレス。

上記の設定ができたら、ターミナルに戻って以下を実行してください。

$ npm run deploy:slack

上記のコマンドでは、「ERC20/scripts/slack.ts」ファイルを実行しているので、何をしているかはファイルを確認してください。

実行してしばらくするとターミナルにログが出力され、通知したいSlackのチャンネルに以下のようなメッセージが届くはずです。

Defender Sentinel ERC20 Token Slack Triggered
Network
goerli
Block Hash
0xd3942fa9218735a1e3a78d3b11402e9e5683f89bc84dbfc0d47e24d21f7717b3
Transaction Hash
0x32ea252ae70d850306e22e18f3fa3730e11eca95e1b213bd87d5d6932fe214e6
Explorer Link
https://goerli.etherscan.io/tx/0x32ea252ae70d850306e22e18f3fa3730e11eca95e1b213bd87d5d6932fe214e6
**Match Reason 1**
Type: Event
Matched Address: 0xb7d1049ff45dd4b8144de5c795adff1b4e7c6e9e
Signature: Paused(address)
Params:
account: 0xC255C784Ac559339A99F91a22E2063ff66B83354
**Match Reason 2**
Type: Function
Matched Address: 0xb7d1049ff45dd4b8144de5c795adff1b4e7c6e9e
Signature: pause()

トランザクションの実行に失敗した場合も以下のように通知されます。

Defender Sentinel ERC20 Token Slack Triggered
Network
goerli
Block Hash
0xd5b678467fb0cd613900dde2fdbe1489c30a4fa876958e622906ec0ac194dc37
Transaction Hash
0xc8ba3b0721bc1723170bc25aefe0ebc0376308891da744049a6c89caaa4be444
Explorer Link
https://goerli.etherscan.io/tx/0xc8ba3b0721bc1723170bc25aefe0ebc0376308891da744049a6c89caaa4be444
**Match Reason**
Type: Function
Matched Address: 0xb7d1049ff45dd4b8144de5c795adff1b4e7c6e9e
Signature: pause()

Etherscanへのリンクやトランザクションハッシュも出力されるので、トランザクションの詳細を確認しやすい点も親切で使いやすいです。

コントラクトが実際に停止されているか確認するために、以下のgoerli etherscanでコントラクトアドレスで検索して、トランザクションを確認してみてください。

以下のように失敗したトランザクションの後で「Pause」関数が実行されていれば成功しています!

最後に

今回は「コントラクト内の特定の関数が実行されたときに、その情報をSlackに通知する機能」の実装を手順を確認しながらしてきました。

いかがだったでしょうか?

Openzeppelin Defender」には他にもさまざまな機能があるので、ぜひ自分でも色々と実装してみてください!

もし何か質問などがあれば以下のTwitterなどからDMしてください!

普段はPythonやブロックチェーンメインに情報発信をしています。

Twiiterでは図解でわかりやすく解説する投稿をしているのでぜひフォローしてくれると嬉しいです!

参考

-Openzeppelin, Smart Contract, Solidity, ブロックチェーン
-,