🌐

Worldcoin Mini Appの開発

2025/01/04に公開

Worldcoin

AIと人間を区別することができるアカウント基盤を提供するブロックチェーン上のサービスです。アカウントに対してAIと人間を区別することができる機能がついています。

Worldcoin Mini App

Worldcoinのアプリ内で利用することができるミニアプリの総称です。

ドキュメント
https://docs.world.org/mini-apps

サクッと試せるテンプレート

Next.jsのテンプレが環境崩れしづらくて一番触りやすい印象
https://github.com/worldcoin/minikit-next-template

各所解説

Worldcoin Mini Appなどミニアプリ系は、アクセスする際に本体アプリと接続することになるので、一番外側(最初に呼び出される部分)がコアになります。

minikit-provider.tsx

Worldcoinの機能は全てMiniKitがインストール済みでないと始まらないので、最初にMiniKit.install()を呼び、各所でMiniKit.isInstalled()を呼びながらフラグチェックをしつつ処理を書いていきます。

"use client"; // Required for Next.js

import { MiniKit } from "@worldcoin/minikit-js";
import { ReactNode, useEffect } from "react";

export default function MiniKitProvider({ children }: { children: ReactNode }) {
  useEffect(() => {
    MiniKit.install();
    console.log(MiniKit.isInstalled());
  }, []);

  return <>{children}</>;
}

Mini App特別機能 command

コマンド名 説明
Verify Orb認証をしているか確認する
Pay 仮想通貨を支払う
Wallet Auth ウォレット接続
Send Transaction トランザクションの送信
Sign Message 署名
Sign Typed Data 署名
Share Social Graph(coming soon) ユーザーの連絡先の公開?
Notifications(coming soon) デバイスへの通知?

使い方は色々あるので今回はPayを解説します。

Payは3つのフェーズで実行します。

  1. nonceの生成
  2. 入力情報の生成(金額と通貨)
  3. コマンドの送信

nonceの生成

const res = await fetch(`/api/initiate-payment`, {
   method: "POST",
});

const { id } = await res.json();

console.log(id);

入力情報の生成

const payload: PayCommandInput = {
  reference: id,
  to: "0x0c892815f0B058E69987920A23FBb33c834289cf", // Test address
  tokens: [
   {
    symbol: Tokens.WLD,
    token_amount: tokenToDecimals(0.5, Tokens.WLD).toString(),
   },
   {
    symbol: Tokens.USDCE,
    token_amount: tokenToDecimals(0.1, Tokens.USDCE).toString(),
   },
  ],
 description: "Watch this is a test",
};

コマンドの送信

if (MiniKit.isInstalled()) {
  return await MiniKit.commandsAsync.pay(payload);
}

ログイン処理の検討

ログイン処理を実装するには2つの方法があります。

  1. OIDCを使った方法
  2. Wallet Authを使った方法
認証方法 World認証 ブロックチェーン処理
OIDC △(Payは使える)
Wallet Auth △(他のコマンドとの組み合わせて補完できる)

基本的にブロックチェーンでの処理を行わない場合(AIと人間を区別するためだけに使う)は1を使用して、ブロックチェーンの処理を行う場合は2を使うのが良いです。

OIDCを用いたログイン処理の実装例

OIDCを用いる場合はNextAuthを用いて実装するのが手軽です。

認証側

app/api/auth/[...nextauth]/route.ts
import NextAuth, { NextAuthOptions } from "next-auth";

const authOptions: NextAuthOptions = {
  secret: process.env.NEXTAUTH_SECRET,

  providers: [
    {
      id: "worldcoin",
      name: "Worldcoin",
      type: "oauth",
      wellKnown: "https://id.worldcoin.org/.well-known/openid-configuration",
      authorization: { params: { scope: "openid" } },
      clientId: process.env.WLD_CLIENT_ID,
      clientSecret: process.env.WLD_CLIENT_SECRET,
      idToken: true,
      checks: ["state", "nonce", "pkce"],
      profile(profile) {
        return {
          id: profile.sub,
          name: profile.sub,
          verificationLevel:
            profile["https://id.worldcoin.org/v1"].verification_level,
        };
      },
    },
  ],
  callbacks: {
    async signIn({ user }) {
      return true;
    },
  },
  debug: process.env.NODE_ENV === "development",
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

ログイン処理側

components/SignIn/index.tsx
"use client";
import { signIn, signOut, useSession } from "next-auth/react";

export const SignIn = () => {
  const { data: session } = useSession();
  if (session) {
    return (
      <>
        Signed in as {session?.user?.name?.slice(0, 10)} <br />
        <button onClick={() => signOut()}>Sign out</button>
      </>
    );
  } else {
    return (
      <>
        Not signed in <br />
        <button onClick={() => signIn()}>Sign in</button>
      </>
    );
  }
};

参考
https://docs.world.org/world-id/sign-in/oidc

Wallet Authを用いたログイン処理の実装例

認証API側

app/api/session/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export async function POST(request: Request) {
    const { walletAddress } = await request.json();

    // セッションクッキーを設定(24時間有効)
    cookies().set("wallet_session", walletAddress, {
        httpOnly: true,
        secure: process.env.NODE_ENV === "production",
        sameSite: "strict",
        maxAge: 60 * 60 * 24, // 24時間
        path: "/",
    });

    return NextResponse.json({ status: "success" });
}

export async function GET() {
    const walletSession = cookies().get("wallet_session");

    return NextResponse.json({
        walletAddress: walletSession?.value || null,
    });
}

export async function DELETE() {
    cookies().delete("wallet_session");

    return NextResponse.json({ status: "success" });
}

ログイン処理側

components/minikit-provider.tsx
"use client"; // Required for Next.js

import { useWalletStore } from "@/store/wallet";
import { signInWithWallet } from "@/utils/signInWithWallet";
import { MiniKit } from "@worldcoin/minikit-js";
import { ReactNode, useEffect } from "react";

export default function MiniKitProvider({ children }: { children: ReactNode }) {
  const setWalletAddress = useWalletStore((state: any) => state.setWalletAddress);

  // 初期化時にセッションをチェック
  useEffect(() => {
    const f = async () => {
      const res: any = await signInWithWallet()
      if (res?.status === 'success') {
        const walletAddress = MiniKit.walletAddress
        if (!walletAddress) return
        setWalletAddress(walletAddress)
      }
    }

    const checkSession = async () => {
      const response = await fetch('/api/session');
      const { walletAddress } = await response.json();

      if (walletAddress) {
        setWalletAddress(walletAddress);
      } else {
        f();
      }
    };

    const timer = setTimeout(() => {
      checkSession();
    }, 1000);

    return () => clearTimeout(timer);
  }, [MiniKit.isInstalled()]);

  useEffect(() => {
    MiniKit.install();
    console.log(MiniKit.isInstalled());
  }, []);

  return <>{children}</>;
}

バグが出て解決しない場合はすぐtelegramのdeveloperグループで聞こう

Worldcoin Mini Appはまだ発展途上のためバグがかなり多いです。そのためコードの問題ではなくSDKの問題であることも多々あります。そのためtelegramのdeveloper groupとdiscordに参加しておくのをお勧めします。

Worldcoin Discord
https://discord.gg/worldnetwork

Worldcoin Telegram
https://t.me/worldcoindevelopers

Discussion