まいにちDapps#17 MUDに入門してAutonomous Worldを触ってみる

ponta

ponta

· 4 min read
Thumbnail

グローバルのハッカソンではAutonomous Worldというテーマがよく聞かれます。フルオンチェーンゲームの解釈を進めたオンチェーンの自律的な世界のことだと認識していますが、どのように作られているのか詳しくないので一緒に見ていきましょう!

Autonomous Worldとは

Autonmous Worldが何か、という話については以下の記事に詳しくまとまっていたのでぜひ参照してみてください。

Autonomous Worldとは

またEthereum naviさんの以下の記事もMUDに限らずオンチェーンゲーム全般の趣旨や課題・可能性について触れていて必読です!

オンチェーンゲームの誕生背景と概要、事例紹介、そして現状の課題から今後の発展可能性までを網羅的に解説

ETH Globalの「Autonomous Worldsハッカソン」でファイナリストに選出された10個のプロジェクトについて概観し、今後の可能性と課題について探る

チュートリアル

今回はこちらのETHGlobalのAutonomous HackathonでのMUD開発者のチュートリアルを進めていきます。

まず https://github.com/latticexyz/react-workshop-starter のレポジトリをgit cloneします。

pnpm install, pnpm devで立ち上げます。

Foundryのlocal chainに繋がったフロントエンドが立ち上がります。Reactのコンポーネントを作るチュートリアルにならないために、コンポーネントは用意してくれています。

Image

チェーンデータをhookしてReact Appに反映・インタラクティブにしていく方法について学んでいきます。

今回はTODOリストを作っていきます。

まずデータストラクチャを作ります。packages/contracts/mud.config.tsを開きます。以下のようにToDoのschemaを追加します。

import { mudConfig } from "@latticexyz/world/register";

export default mudConfig({
  tables: {
    ToDo: {
      schema: {
        done: "bool",
        body: "string",
      },
    },
  },
  modules: [
    {
      name: "UniqueEntityModule",
      root: true,
      args: [],
    },
  ],
});

自動でコントラクトが再デプロイされるようです。

次にpackages/contracts/src/systems/ToDoSystem.solを開いて編集していきます。

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { System } from "@latticexyz/world/src/System.sol";
import { addressToEntity } from "../Utils.sol";
import { getUniqueEntity } from "@latticexyz/world/src/modules/uniqueentity/getUniqueEntity.sol";

import { ToDo, ToDoData } from "../codegen/Tables.sol";

contract ToDoSystem is System {
    function addToDo(string memory body) public {
        bytes32 id = getUniqueEntity();

        ToDo.set(id, ToDoData({body: body, done: false}));
    }
}

todoを追加するためのaddToDoという関数を作成しました。

フロントエンドからこの関数を実行していきます。

コンポーネントは作成されているので、インポートします。

packages/client/src/App.tsx

import React from "react";
import "./index.css";
import {
  AppContainer,
  Container,
  Card,
  HeaderDiv,
  Subtitle,
  Title,
  Footer,
  TextLink,
} from "./theme";
import { ToDoForm } from "./ToDoForm";

export const App = () => {
  return (
    <Container>
      <AppContainer>
        <HeaderDiv>
          <Title>MUD x React Workshop</Title>
          <Subtitle>Creating a todo list using MUD</Subtitle>
        </HeaderDiv>

        <Card>
          <ToDoForm />
        </Card>

        <Footer>
          <TextLink href="https://v2.mud.dev">MUD docs</TextLink>
        </Footer>
      </AppContainer>
    </Container>
  );
};

Image

上記の+ボタンを押しても何も起きないのでロジックを実装していきます。

ToDoForm.tsx

import { useState } from "react";
import { FormButton, FormField, FormFieldWrapper } from "./theme";
import { Plus } from "./theme/Plus";
import { useMUD } from "./MUDContext";

export function ToDoForm() {
  const [newToDo, setNewToDo] = useState("");

  const {
    systemCalls: { addToDo },
  } = useMUD();

  return (
    <FormFieldWrapper>
      <FormField
        type="text"
        placeholder="new todo"
        value={newToDo}
        onChange={(e) => {
          setNewToDo(e.target.value);
        }}
      />
      <FormButton
        onClick={() => {
          addToDo(newToDo);
          setNewToDo("");
        }}
      >
        <Plus />
      </FormButton>
    </FormFieldWrapper>
  );
}

状態を確認するためにフロントに表示します。

import React from "react";
import "./index.css";
import {
  AppContainer,
  Container,
  Card,
  HeaderDiv,
  Subtitle,
  Title,
  Footer,
  TextLink,
} from "./theme";
import { ToDoForm } from "./ToDoForm";
import { useMUD } from "./MUDContext";
import { useEntityQuery } from "@latticexyz/react";
import { Has } from "@latticexyz/recs";

export const App = () => {
  const {
    components: { ToDo },
  } = useMUD();

  const toDoIds = useEntityQuery([Has(ToDo)]);

  return (
    <Container>
      <AppContainer>
        <HeaderDiv>
          <Title>MUD x React Workshop</Title>
          <Subtitle>Creating a todo list using MUD</Subtitle>
        </HeaderDiv>

        <Card>
          {toDoIds}
          <ToDoForm />
        </Card>

        <Footer>
          <TextLink href="https://v2.mud.dev">MUD docs</TextLink>
        </Footer>
      </AppContainer>
    </Container>
  );
};
Image

id 0x01が表示されました。idからbodyに入れたstringのデータを取得して表示させます。

<Card>
  {[...toDoIds].map((id) => {
    const toDoData = getComponentValueStrict(ToDo, id);
    return <ToDoItem key={id} id={id} {...toDoData} />;
  })}
  <ToDoForm />
</Card>

Image

ここまででaddToDoは完成です。次にチェックボックスを入れたらToDoをDoneにする必要があります。またSolidityから編集していきます。

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { System } from "@latticexyz/world/src/System.sol";
import { addressToEntity } from "../Utils.sol";
import { getUniqueEntity } from "@latticexyz/world/src/modules/uniqueentity/getUniqueEntity.sol";

import { ToDo, ToDoData } from "../codegen/Tables.sol";

contract ToDoSystem is System {
    function addToDo(string memory body) public {
        bytes32 id = getUniqueEntity();

        ToDo.set(id, ToDoData({body: body, done: false}));
    }

    function toggleDone(bytes32 toDoId) public {
        ToDo.setDone(toDoId, !ToDo.getDone(toDoId));
    }
}

保存するとコントラクトが自動デプロイされフロントエンドは新しいコントラクトに接続されています(既存のToDoは無くなって見えます)。新しいworldに切り替わったので、古いworldに戻したければ戻すこともできるみたいです。

createSystemCalls.ts

const toggleDone = (id: string) => {
  worldSend("toggleDone", [entityToBytes32(id)]);
};

ToDoItem.tsx

export function ToDoItem({ id, body, done }: Props) {
  const {
    systemCalls: { toggleDone },
  } = useMUD();
  return (
    <ToDoItemWrapper>
      <span>{body}</span>
      <input
        type="checkbox"
        checked={done}
        onChange={(e) => {
          toggleDone(id);
        }}
      />
    </ToDoItemWrapper>
  );
}
Image

TodoをDoneにすることができました。

次にチームでToDoを管理することを念頭に、TodoにOwnerをつけていきます。

function addToDo(string memory body) public {
    bytes32 id = getUniqueEntity();
    bytes32 owner = addressToEntity(_msgSender());

    ToDo.set(id, ToDoData({body: body, done: false, owner: owner}));
}

MUDではmsg.sender()ではなく_msgSender()を使う必要があります。

ここで少しエラーが出てしまって今回のチュートリアルはここまでとなりました。。。

ただ全体的な流れと基本的な操作はつかめたと思います。

まとめ

これでゲーム全体を作るのはなかなか根気のいる作業だなという所感ですが、Autonomous Worldを作るにあたってMUDは欠かせないツールだなと思いました。今後の開発体験の向上に期待です。

弊社Pontechはweb3に関わる開発を得意とするテック企業です。サービス開発に関するご相談はこちらのフォームからお願いいたします。

また、受託開発案件に共に取り組むメンバーを募集しています!ご興味のある方はぜひお話させてください!

ponta

About ponta

2019年からEthereumを中心にDapp開発に従事。スキーとNBAとTWICEが好き。

Copyright © 2024 Pontech.Inc