見出し画像

Build on Sui 第3・4回のハンズオン復習

第2回までで、NFTのコントラクトをデプロイしてSui CLIからNFTをMINTすることに成功しましたね✨️

第3回・第4回はNFTのコントラクトを実際に書いてデプロイ、フロントエンドを作成して接続する。ここまでを進めました✨️

第3回のイベントでは別のリポジトリを使用しましたが、この記事では3回・4回を同時に説明したいので、以下の第4回に使用したサンプルコードを使用して3〜4回の分を解説していきますね!!

いつも通り、講義内容は割愛し、ハンズオンの方法のみに特化しています。
講義内容については講義資料をご確認ください。事前に確認していただくことで、より学習が深まります✨️

第3回のPDF資料

第4回のPDF資料

Workshop用のリポジトリのクローン

クローンの仕方は第1回の記事にて紹介していますので割愛させていただきます。以下のサンプルコードをクローンしてみましょう✨️
フォーク可能な方はフォークして魔改造していただいてもOKです!

クローンの仕方がわからない方はこちらの記事に解説あり👇️

フォークって何よ?って方はこちら👇️

Windowsの方で全然動かなくてお困りの方は以下2つの記事でWSLを導入するとかなりスムーズに今後の開発が進みますので、はじめは少し大変ですがオススメです。(イベントでは開発環境を整えるサポートも全力で行っていますので、お困りの方は是非参加してね✨️)

クローンしてVS Codeで開いてこのような感じになっていたら準備完了!!

READMEを確認してみよう

まずはクローンしたコードのファイルの構成を知るために、READMEを見てみましょう。多くのプロジェクトではREADMEファイルが置いてあることが多く、ここにどんな内容のコードなのか、という説明書が入っています。

では一番下の方のREADME.mdを右クリックしてプレビューを開くで開いてみましょう。

説明書みたいなのが出てきましたね👀説明書、よく読みましょう👀

まずはContractsの部分にSui Moveのコントラクトが書いてあるよ〜という説明があるので覗いてみましょう。Contracts→Sources→nft.moveとあるのでnft.moveを開いてみてください✨️これがこのプロジェクトのNFTのコントラクトです。中身も貼っておきます。解説が細かく書いてありますので、是非じっくり読んでみてください。

nft.moveの中身

/// ワークショップ向けの最小構成 NFT コントラクトです。
/// 学べること:
/// - オブジェクト(object)ベースの NFT をミントする方法
/// - エントリ関数でミントした NFT をウォレットへ転送する手順
/// - Display(表示用メタデータ)の初期化と Publisher の請求方法
/// 注意: 学習用サンプルのため、権限制御や高度な検証は意図的に最小限です。
module nft_sample::nft;

// よく使う標準/フレームワークのモジュールを `use` して短く呼べるようにします。
use std::string::String;
use sui::display;
use sui::package;

// Display 初期化時の Publisher 請求に使うワンタイムウィットネス。
// パッケージ publish 時に自動で与えられ、`package::claim` に使います。
public struct NFT has drop {}

// ミントされる NFT 本体。
// - `key` 能力: Sui台帳で「独自のIDを持つオブジェクト」であることを示します。
// - `store` 能力: 他モジュールへ移動(転送)できるようにします。
public struct WorkshopNFT has key, store {
    id: UID,          // Sui が管理するユニークID(必須)
    name: String,     // 表示名
    description: String, // 説明文
    image_url: String,// 画像URL(IPFSやHTTPSなど)
    creator: address,         // 作成者(ミント時の送信者)
}

// 入力バリデーション用のエラーコード。
const EEmptyName: u64 = 1;
const EEmptyImageUrl: u64 = 2;

// パッケージ publish 時に Display を作成・共有する初期化ロジック。
// Suiのinitializerは `fun init(...)` をモジュール内に定義するだけでOK(属性は不要)。
fun init(witness: NFT, ctx: &mut TxContext) {
    // Publisher を取得(Display の登録に必要)
    let publisher = package::claim(witness, ctx);

    // 表示に使うフィールドテンプレートを登録
    let mut disp = display::new_with_fields<WorkshopNFT>(
        &publisher,
        vector[
            b"name".to_string(),
            b"description".to_string(),
            b"image_url".to_string(),
            b"link".to_string(),
        ],
        vector[
            b"{name}".to_string(),
            b"{description}".to_string(),
            b"{image_url}".to_string(),
            b"{link}".to_string(),
        ],
        ctx,
    );

    // Display のバージョンを進めて有効化し、作成者に転送
    disp.update_version();
    transfer::public_transfer(disp, ctx.sender());
    
    // Publisher を発行者へ返す(保有しておきたいケースが多い)
    transfer::public_transfer(publisher, ctx.sender());
}

// イベント発行は学習をシンプルにするため削除しました。
// その代わり、ミント処理の結果はそのままウォレットに転送します。

// ウォレット送信者に NFT をミントするエントリポイント。
// - 受け取った `name`/`description`/`image_url` を検証してミント
// - できあがった NFT オブジェクトを送信者へ転送
entry fun mint(
    name: String,
    description: String,
    image_url: String,
    ctx: &mut TxContext,
) {
    let nft = mint_internal(name, description, image_url, ctx);
    // `store` 能力があるためどこからでも安全に転送できます。
    transfer::public_transfer(nft, ctx.sender());
}

// 実際のミント処理(ロジック部分)。
// - 入力値を簡単にチェック
// - `object::new(ctx)` で新しいオブジェクトIDを割り当て
// - フィールドを詰めて `WorkshopNFT` を返す
fun mint_internal(
    name: String,
    description: String,
    image_url: String,
    ctx: &mut TxContext,
): WorkshopNFT {
    // ここでは最低限のチェックだけを行います。
    assert!(!name.is_empty(), EEmptyName);
    assert!(!image_url.is_empty(), EEmptyImageUrl);

    WorkshopNFT {
        id: object::new(ctx),
        name,
        description,
        image_url,
        creator: ctx.sender(),
    }
}

コントラクト(Package)を自分で作ってみる場合

Suiのプロジェクトを始める際、絶対覚えておいてほしいコマンド、sui move newをやってみましょう✨️

sui move new プロジェクト名

ターミナルでこれを実行してみましょう!!今回はnft_workshopというプロジェクト名でやってみますね!!

実行してみると・・・・nft_workshopというフォルダが出来上がってますね。

中には、

こんな感じで、このコマンドは最小構成のSui Moveパッケージを作ることができます。主な生成物は次のとおり。

  • Move.toml:設定ファイル

  • sources/:本体コード置き場。.move モジュールを配置します。

  • tests/:ユニット/仕様テスト用。.move で #[test] が使えます。

  • .gitignore:ビルド成果物を除外するためのもの。

これでSui Move開発の第一歩を歩むことができました✨️

sources/の中に「nft_workshop.move」というmoveのファイルが出来上がっていていますね!!コメントが入っていますが、中身はまだ空っぽです。コメントを消して、コードを書き始めてください✨️

contracts/のnft.moveを写経するだけでもとても勉強になると思いますよ👀

⚠️注意点
最初のModule宣言は作成したプロジェクト名と原則同じにしてください。
今回の場合、
module nft_sample::nft; ❌️
module nft_workshop::nft; ⭕️
となるので写経する場合も、こちらだけは変更してね✨️

(実際はMove.tomlファイルで変更可能ですが今回は説明割愛)

すべて写経を終えたら、ちゃんとPublish(デプロイ)できるのかもためしてみてください👀

nft_workshop.moveの中身、埋まりましたか!?自分で完璧にかけるようになる必要はありません。どこでどんなコードを書いているのか、なんとなく理解できるようになればOKです✨️

今はAIもありますし、資料もたくさんあるし、特にテストがあるわけでもないので、ざっくり雰囲気理解できることがとっても大切だと思います。

 MoveコントラクトをPublishしてみよう!

ではコントラクトをPublishしていきましょう!!第1〜2回でも行ったようなWalletアドレスのインポートなどを進めていきますね✨️

テストネット $SUI が入っているアドレスをインポートしましょう🔥

・ウォレットのインポート

 sui keytool import "<ここに12語もしくは24語のシードフレーズ>" ed25519

・インポートしたアドレスをアクティブに

sui client switch --address <ADDRESS>

Switch Addressのコマンドを打つと
①[y/N]みたいなのが出てくるのでyを打ってEnter
②その次、Sui Full node.......URLみたいなのが出てくるので、何も打たずにEnter
③Selectなんちゃら〜〜みたいなのが出てくるので0を打ってEnter

すると、switchedなんちゃら〜〜〜って出てくるのでそれが出てきただ大丈夫!!

・次、テストネット環境にスイッチ

sui client switch --env testnet

ガス代の確認

sui client gas

ガス代がない場合はfaucetサイトから請求してください🙇

ここまでで、デプロイの準備は完了✨️

つぎに、Packageがあるところでターミナルを開きます。nft_workshopのところで右クリックをして統合ターミナルで開くを押してください。

ターミナルの最後がnft_workshop# になっていたら正解です✨️

こちらで、前回も行ったPublishコマンドを実行してみましょう👀

sui client publish --gas-budget 100000000

こんな感じになったらデプロイできてるよ🔥

フロントエンドに挑戦

ここからは第4回の内容です✨️
mint_site_workshopというフォルダを使います。

・mint_site→ミントサイトの本体(正解のサンプルコード)
・mint_site_workshop →未完成のミントサイトのコード、皆様で完成させてほしい。

まずはmint_site_workshopをクリックして中身を見てみましょう。

この中にもREADME.MDがありますのでプレビューで開いてみてください✨️

おっと、、ここには演習用の課題が書かれていますね✨️第4回のイベントではこちらを進めて行きました。この記事でもこちらのREADME.mdに沿って課題を進めていきます。開いたままにしておいてくださいね✨️

では、まずはお手本用mint_siteを見てみましょう。
mint_siteのフォルダの上で右クリック、統合ターミナルで開くを押してターミナルを開きます。

つづいて開いたターミナルで下記のコマンドを実行してみましょう。

pnpm dev

実行するとhttps://locakhostなんちゃら〜〜っていうのが出てきますので、こちらを開いてみましょう。開き方はCtrl または⌘キーを押しながらクリックです。

こんなサイトが出ますね✨️ TestnetでWalletをConnectしてみたり、NFTをMINTしてみたりして動作確認をしてみましょう。こちらがお手本のサイトです。

確認が終わったら、同じターミナル上でCtrl+Cのキーを同時押しして、ローカルホストを閉じましょう。これが残っていると後で自分の作ったサイトの動作確認をする際にエラーが出てしまいます。

ではさっそくmint_site_workshopの方をどんどん完成させていきましょう。

課題1に関しては、ほとんど第3回の復習となりますので今回は前半を割愛します。手順C:の部分からやっていきましょう✨️

なお、今回の演習ではPackage IDはこちらを使ってください。(Packageは事前にデプロイしてあります)

0x2fcf1119861d090935d6edfe815cc6fd699f01a1a95154f082a06fa32aaefb1d

課題1:環境変数の設定

今回はViteというフロントエンド開発用のビルド&開発サーバーツールを使用してサイトを作成しています。Viteについては説明は割愛させていただきますが、下記記事を見るとわかりやすいと思います✨️

.envファイルは、自分の環境固有の値を入れる設定メモ。
VITE_NETWORK や VITE_PACKAGE_ID などを dev server 起動時に Vite が読み取り、フロントのコードから import.meta.env 経由で参照できるようにします。

どのPackageを呼ぶの?実行するのはmainnetなのかtestnetなのか?とかそういった情報を入れます。

mint_site_workshopの.envを開いて=の先の部分を埋めましょう。

簡単でしたね?答えはmint_siteの方の.envを確認してください。

課題2:Walletの情報を取得・表示しよう

mint_site_workshop→src→WalletStatus.tsxの中身を完成させましょう。

  1. @mysten/dapp-kit から useCurrentAccount をインポートする。

  2. フックを呼び出して const account = useCurrentAccount(); のように現在のアカウント情報を取得する。

  3. account が存在する場合は「接続中」である旨とウォレットアドレスを表示し、存在しない場合は「未接続」と表示する。

    • アドレスは account.address から取得できます。

    • 表示形式に迷ったら、0x1234...abcd のように短縮表示しても構いません。

こちらのドキュメントを参考に完成させてみましょう✨️わからない場合はmint_siteの方のWalletStatus.tsxに答えがありますよ👀

課題3:MintButton.tsxでミント用のトランザクションを完成させよう。

第2回では直接Sui CLIでmove callを行い、NFTをMINTしましたよね!今回はTypeScript SDKを使ってmint関数を呼び出すコードを追記します。

mint_site_workshop→src→MintButton.tsxを完成させてください✨️

参考記事はこちらです✨️

一通り作業が完了したら、ちゃんと動作するか確認してみましょう。
mint_site_workshopのところで右クリックして統合ターミナルを開いてください。

開いたところでpnpm iというコマンドを実行してください。
こちらはpnpmをインストールするためのコマンドです。

pnpm i

インストールが完了したら、最初にお手本サイトを起動した時と同じコマンドを実行します。

pnpm dev

 実行するとまた先程のようにURLが出てきますのでLocalの方を開いて確認してください✨️

Walletを接続すると「Wallet connected」の文字と繋いだAddressが表示されていて、自分の設定した名前・説明・画像URLのNFTがMINTできれば完璧です✨️

お疲れ様でした〜〜〜〜(T_T)

いいなと思ったら応援しよう!