SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

新人がDAO(分散型自律組織)を構築してみた

・橘高 圭亮

2022年度新卒入社の4人で、DAOを実現できるアプリケーションの構築に挑戦しました。

DAOは、企業のような従来の中央集権型ではなく、誰でも参加でき、個人が組織の意思決定の権限を持てる民主主義的な組織として注目されています。自分と共通の目標を掲げた組織に気軽に参加できるDAOの考え方は、ジョブ型雇用や1つの組織に縛られない働き方などの近年のトレンドに乗り、今後拡大していく可能性があります。しかしながら、インターネット上のDAOに関する情報は概念的な説明が多く、具体的なサービスや実装のイメージを掴みにくい現状があります。

そこで本記事では、実際にDAOの概念を用いたサービスのプロトタイプ開発を通して、基本的なDAOの思想とその実装方法を簡単にご紹介します。

DAOの概要

DAOとは、分散型自律組織(Decentralized Autonomous Organization)の略で、共通の目的のために集団が共同で所有し、ブロックチェーンで管理されている組織です1)https://ethereum.org/ja/dao/

DAOに参加するためには、ガバナンストークンと呼ばれる、DAO内での投票権となる独自トークンを保有する必要があります。ガバナンストークンは、DAOへの貢献度など、あらかじめDAO内で定められたルールに従って付与されます。ガバナンストークンを用いた投票で意思決定を行うことで、権力者によるトップダウン式ではなく、参加者が共同で所有する組織を実現します。

ガバナンストークンの付与には、スマートコントラクトを使用する事が一般的です。スマートコントラクトとは、ブロックチェーンに保存された改ざん不可能なプログラムのことで、あらかじめ設定されたルールに従って決められた処理を実行します。スマートコントラクトで定められたルールに従って取引されることで、自動的にブロックチェーン上の所有者が書き換えられ、改ざん不可能な取引履歴を持つことが可能です2)https://www.nri-digital.jp/tech/20230221-12747/

プロトタイプの実装

アプリケーションの概要

まず、DAOの概念をアプリケーションで実現するために、今回作成するアプリケーションが満たすべき要件を自分たちの理解の上で整理しました。

  • ガバナンストークンがオンチェーンに存在する。
  • ガバナンストークンは、DAOへの貢献を行うことで、スマートコントラクトで参加者に自動的に付与される。
  • ガバナンストークンを用いて意思決定を行うことができる。

以上の要件を踏まえ、ガバナンストークンを用いて投票を行うことができるデモアプリケーションを作成しました。

構成図

構成は下の図の通りです。React+Spring bootをベースにして機能を実装し、機能の利用をトリガーとしてReactからスマートコントラクトが呼び出しされるような構成にしました。

機能一覧

今回のプロトタイプではガバナンストークンによる組織の意思決定を行うために、意思決定のための投票機能とトークン獲得のためのメッセージ機能を作成しました。

投票機能

ガバナンストークンを用いて投票を行うことができる。

  1. 提案の作成
    投票テーマ(提案)の作成時に、提案者はガバナンストークンを消費する。
  2. 投票
    提案への投票時に、投票者はガバナンストークンを消費する。
  3. 投票数に応じたトークン付与
    「DAO参加者の関心を引くような提案を行っている=DAOに対する貢献をしている」とみなし、投票が終了した際に、提案者は提案に対する総投票数に比例したガバナンストークンを獲得できる。

メッセージ機能

DAOへのメッセージ投稿を行うとトークンを獲得できる。

主に新規参入者のガバナンストークン獲得を支援するための機能である。

  1. メッセージ投稿
    DAOメンバーに対して、感謝を伝えるメッセージ(ありがとうメッセージ)をDAOへ投稿する。
  2. 一定額のトークン付与
    メッセージ送信者に対して一定額のトークンを付与する。
トークンの流れ図

完成したプロトタイプの画面

細かい実装方法をご説明する前に、完成したプロトタイプのメイン機能である投票画面を紹介します。

投票者は、画面下部の選択肢に対応するボタンを押すことで投票を行います。ボタンを押すとメタマスク経由でスマートコントラクトが呼び出され、これから実装するスマートコントラクトのうちの一つ、「UseToken」メソッドが実行されます。

下記の画像は、Etherscanというイーサリアム上の取引状況を確認できるサイトから、作成したガバナンストークンで実行されたスマートコントラクトを確認した結果です3)https://etherscan.io/

投票を契機に、投票者のウォレットアドレスから2トークン消費されている事が確認できます。

スマートコントラクトの実装方法

具体的な実装方法を詳しく解説していきます。

スマートコントラクトの記述には、Solidityという、スマートコントラクトの記述に特化した言語を用いました。HardhatはSolidityの開発環境ツールで、スマートコントラクトを作成・デプロイするうえで便利な機能がそろっています。

今回は、SolidityとHardhatを用いて作成したスマートコントラクトを、Alchemyが提供しているイーサリアムノードを用いてイーサリアム上にデプロイしました。

実装方法

React+Spring boot部分は、一般的なwebアプリケーションと同じなので割愛し、スマートコントラクトの実装からReactと連携するまで中心に紹介します。

①スマートコントラクトの実装・デプロイ

まず、Hardhatのチュートリアルを参照しながら、スマートコントラクト開発環境を構築します4)https://hardhat.org/

その後、Solidityでスマートコントラクトを実装していきます。今回はOpenZeppelinが提供している、ERC20トークンクラスを利用しました。ERC20とは、イーサリアム上の基本的なトークンの規格で、この規格に則る事で簡単にトークンを開発できます。

Token.sol
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";

contract Token is ERC20, ERC20Burnable, ERC20Permit, ERC20Votes {

    // トークンの流通量を格納する変数
    uint256 total = 0;
  
    // コンストラクタ
    constructor() ERC20("DX2Token", "DX2") ERC20Permit("DX2Token") {
         total += 1000 * 10 ** decimals(); 
        _mint(msg.sender, 1000 * 10 ** decimals());
    }

  // トークンの発行(Mint)
    function getToken(uint256 amount) external {
        total += amount  * 10 ** decimals(); 
        _mint(msg.sender, amount * 10 ** decimals());
    }
  
  // トークンの消費(Burn)
    function useToken(uint256 amount) external {
         total -=  amount * 10 ** decimals(); 
        _burn(msg.sender, amount * 10 ** decimals());
    }

  // トークンの流通量の参照
    function getTotalToken() public view  returns (uint256) {
         return total;
    }

  // トークンの発行(Mint)
    function _mint(address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._mint(to, amount);
    }

  // トークンの消費(Burn)
    function _burn(address account, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._burn(account, amount);
    }
}

その後のfunctionは、スマートコントラクトの実装となります。

トークンの発行・消費はライブラリで実装されているため、ライブラリの関数を呼び出します。発行・消費の際に、totalという変数に加減を実施し、トークンの発行量が取得できるように実装しました。

最後にデプロイをしていきます。

デプロイの準備として、Alchemyとアプリケーションの連携を行います。今回はデモアプリですので、メインネットではなく、テストネット上を利用するように設定しました5)https://www.alchemy.com/。上記のサイトで取得したURLと、自分のメタマスクウォレットアドレスをhardhat.config.jsに記載することで連携が完了します。

hardhat.config.js
require("@nomiclabs/hardhat-waffle");

module.exports = {
  defaultNetwork: "goerli",
  solidity: "0.8.17",
  networks: {
    goerli:{
      url: "Alchemyで発行したHTTPS用URL",
      accounts:[
        "デプロイするウォレットアドレス",
      ],
    },
  },
};

上記の準備が完了したら、Hardhatのチュートリアルの通り、デプロイ用コードを記述・実行することでデプロイ完了です。

②スマートコントラクトとReactの連携

デプロイが完了すると、コンソール上にコントラクトアドレスが表示されると同時に、ABI(アプリケーションバイナリインターフェイス)と呼ばれるJSONファイルが生成されます。先ほど作成したスマートコントラクトが、バイナリ実行できるようにJSONに変換されているファイルです。

このABIをReact側ソースファイル配下にコピーし、コントラクトアドレスとともに記述することで、Reactとの連携の準備が整います。

connect.js
import abi from "コピーしたABIのパス";

export const tokenAbi = abi.abi;
export const contractAddress = "コントラクトアドレス";
③React側でのスマートコントラクトの呼び出し

まず、公式ドキュメントを見ながら、スマートコントラクトの取得を行います6)https://docs.ethers.org/v5/。呼び出しには、ethersライブラリを利用します。この中で、先ほどコピーしてきたコントラクトアドレスとABIを利用しています。

import { ethers } from "ethers";
import { contractAddress, tokenAbi } from "connect";
import { createContext } from "react";

export const TokenContext = createContext();

// スマートコントラクトの取得をするメソッド
const getSmartContract = () => {

    // MetaMaskと連携
    const provider = new ethers.providers.Web3Provider(window.ethereum);

    // Signerの取得
    const signer = provider.getSigner();
    const signerAddress = signer.getAddress();

    // スマートコントラクトを取得
    const tokenContract = new ethers.Contract(
        contractAddress,
        tokenAbi,
        signer
    );

    console.log(provider, signer, tokenContract);
    return tokenContract;
};

上記で取得したスマートコントラクトの呼び出しは以下のように行います。

この変数を利用することで、React内でスマートコントラクトを実行する事が可能になります。

// トークンを付与するメソッド
const getToken = async (token) => {
    const tokenContract = getSmartContract();
    tokenContract.getToken(token);
};

ちなみに、ユーザーのメタマスクアカウントとアプリケーションを連携するには、以下のようなコードを記述します。window.ethereumオブジェクトを利用すると、メタマスクのインストール確認やアカウントとの接続を行えます。

const { ethereum } = window;
const [currentAccount, setCurrentAccount] = useState("");

// メタマスクウォレットとの連携
const connectWallet = async () => {
    if (!window.ethereum) return alert("メタマスクをインストールしてください。");

    //メタマスクアカウントとの接続を開始
    const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
    setCurrentAccount(accounts[0]);
};

終わりに

DAOは、組織の新たな形態として注目されており、本記事で紹介した方法以外にも簡単にDAOを形成するためのサービスが次々登場しています。今回、新人同士でDAOの企画~実装を行った結果、技術力以上にDAO内でトークンが循環するようなエコシステムを考案する事が重要だと感じました。

今後DAOの拡大が期待されていますが、本記事がその一助になれば幸いです。

References   [ + ]