mcpField
S
solana-mobile-wallet

solana-mobile-wallet

github.com/solana-mobile開発812.6k 回インストール1 週間前に更新
リポジトリ

solana-mobile-wallet について

Mobile Wallet Adapter と Wallet UI React Native Kit を使い、ウォレット接続、チェーンデータの取得、トランザクション署名を行う Solana モバイルアプリ向けスキルです。Kit と旧 web3.js の選択や、よくある実装上の問題も扱います。

主な機能

  1. 01モバイルウォレットの接続と切断
  2. 02Solana のチェーンデータ取得
  3. 03トランザクションの署名と送信
  4. 04クラスター設定とクエリキャッシュ
  5. 05接続や署名のトラブルシューティング

活用シーン

  1. 01React Native アプリへのウォレット接続追加
  2. 02残高取得とチェーンデータの表示
  3. 03Solana トランザクションの作成と送信

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/solana-mobile/solana-mobile-skills --skill solana-mobile-wallet

インストール後、Claude Code で/solana-mobile-walletを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
kit.md
troubleshooting.md
web3js.md

詳細

モバイルの Solana ウォレット

Mobile Wallet Adapter(MWA)を通じたウォレット接続とトランザクション署名を、@wallet-ui/react-native-kit(レガシースタックでは @wallet-ui/react-native-web3js)でラップします。

MWA を Android で利用するには開発ビルドが必要です。Expo Go は動作しません。 まだ開発ビルドがない場合、またはプロジェクトが存在しない場合は、solana-mobile スキルから始めてください。

Step 1: スタックを選ぶ — コードを書く前に選んでください

Kit のコードを書いてください。 @solana/kit と @wallet-ui/react-native-kit が選ぶべきスタックで、このファイルの内容はすべてこのスタックについて説明しています。

@solana/web3.js を使う唯一の理由は、プロジェクトがすでにそれを使っていることです。まず package.json を確認してください。

package.json の記載対応
@wallet-ui/react-native-kit、または Solana クライアントがまだないKit。このファイルと references/kit.md
@wallet-ui/react-native-web3js がアプリの Solana クライアントreferences/web3js.md
ユーザーが web3.js を明示的に希望しているreferences/web3js.md を参照し、Kit のほうが適している理由も伝えてください

Kit のプロジェクトに web3.js を導入したり、1 つのアプリで両方を混在させたりしないでください。プロバイダーの props、フックの戻り値、トランザクションの構築方法がそれぞれ異なるため、一方のコードをもう一方で使うと、気づかないうちに失敗します。プロジェクトに Solana クライアントがまったくない場合は、新規構築です。Kit を使ってください。

既存の web3.js アプリに新しいウォレット機能を追加する場合、作業の途中で移行する必要はありません。既存の構成に合わせ、移行に価値がありそうであれば、今後の対応として提案してください。

Step 2: プロバイダーがマウントされていることを確認する

useMobileWallet は、その上位に MobileWalletProvider がないと空の状態を返します。ルートレイアウトまたはアプリプロバイダーモジュールで確認してください。

SolanaCluster には label も必要です。ヘルパーがこれを設定するため、手動ではなく createSolana* ヘルパーを使ってクラスターを構築してください。

import {
  type AppIdentity,
  createSolanaDevnet,
  MobileWalletProvider,
  type SolanaCluster,
} from '@wallet-ui/react-native-kit'

const identity: AppIdentity = { name: 'My App' }
const cluster: SolanaCluster = createSolanaDevnet({ url: 'https://api.devnet.solana.com' })

<MobileWalletProvider cluster={cluster} identity={identity}>
  {children}
</MobileWalletProvider>

createSolanaDevnet、createSolanaTestnet、createSolanaLocalnet は省略可能な props を受け取ります。createSolanaMainnet には url が必要です。mainnet には適切なデフォルトのパブリック URL がないためです。

プロバイダーの props は cluster、identity と、省略可能な cache、createClient、children です。chain prop も endpoint prop もありません。これらを渡しても何も起きません。

すべての AppIdentity フィールドは省略可能です。始めるには name だけで十分です。リリースするものには、実際のディープリンクとなる uri を追加してください。ウォレットの認証時に表示されるためです。プレースホルダーを指定すると、フィッシングの試みと受け取られる可能性があります。

ウォレットプロバイダーの上位に、@tanstack/react-query の QueryClientProvider を配置してください。以下のフックのパターンでは、クエリとミューテーションを使用します。

Step 3: フックを使う

import { useMobileWallet } from '@wallet-ui/react-native-kit'

const { account, connect, disconnect, client } = useMobileWallet()

kit スタックでフックが実際に返す値は次のとおりです。

値型備考
accountAccount | undefined未接続時は null ではなく undefined です
accountsAccount[] | null認証済みのすべてのアカウントです
connect() => Promise<Account>ウォレット選択画面を開きます
disconnect() => Promise<void>
clientClientKit クライアントです。RPC 呼び出しには client.rpc を使います
chainSolanaClusterIdアクティブなクラスターです。クエリーキーに含めてください
sendTransactions(instructions: Instruction[]) => Promise<string>最も簡単な送信方法です
signAndSendTransaction(tx, minContextSlot) => Promise<SignatureBytes>2 番目の引数に注意してください
signTransaction(tx) => Promise<Transaction>ブロードキャストせずに署名します
signMessages(msg: Uint8Array) => Promise<Uint8Array>
signIn(payload) => Promise<SignInOutput>Solana でサインインします。接続もできます
identity、store設定と認証ストアです

これらのいくつかには単数形のエイリアス(sendTransaction、signMessage、 signTransactions)もあり、シグネチャは同じです。テンプレートでは複数形を使っていますが、 どちらでも動作するため、プロジェクトですでに使われている形式に従ってください。

つまずきやすい点は 4 つあります。

  1. connected というブール値はありません。 const connected = !!account のように導出します。
  2. signAndSendTransaction の 2 番目の引数は minContextSlot です。 トランザクションだけを渡して呼び出すと失敗します。getLatestBlockhash からスロットを取得するか、処理を代行してくれる sendTransactions(instructions) を使ってください。
  3. kit フックが公開するのは connection ではなく client です。 connection があるのは web3.js スタックだけです。
  4. チェーンデータを保持するすべての React Query キーに chain を含めてください。 そうしないと、クラスターの切り替え後も前のネットワークの残高がキャッシュから返され、ウォレットの不具合のように見えてしまいます。

account.address は kit の Address(ブランド付き文字列)なので、そのままテキストに埋め込めます。 account.label はウォレットから提供される名前で、未定義の場合があります。

接続と切断

import { Pressable, Text } from 'react-native'
import { useMobileWallet } from '@wallet-ui/react-native-kit'

export function ConnectButton() {
  const { account, connect, disconnect } = useMobileWallet()

  async function onPress() {
    try {
      if (account) await disconnect()
      else await connect()
    } catch (error) {
      // The user dismissing the wallet picker lands here. Do not treat it as a crash.
      console.error(error)
    }
  }

  return (
    <Pressable onPress={onPress}>
      <Text>{account ? 'Disconnect' : 'Connect Wallet'}</Text>
    </Pressable>
  )
}

connect() は必ず try/catch で囲んでください。ウォレット選択画面をキャンセルすると、プロミスが拒否されるためです。 キャンセルは通常の結果であり、ユーザーに警告するようなエラー状態ではありません。キャンセルと実際の失敗を区別する方法は references/kit.md を参照してください。

認証情報はキャッシュされるため、アプリを再起動しても新しいプロンプトなしで再接続されます。

チェーンデータの読み取り

フックの client を使います。自分でクライアントを構築する必要はありません。

import type { Address } from '@solana/kit'
import { useQuery } from '@tanstack/react-query'
import { useMobileWallet } from '@wallet-ui/react-native-kit'

export function useGetBalance({ address }: { address: Address }) {
  const { chain, client } = useMobileWallet()

  return useQuery({
    queryFn: () => client.rpc.getBalance(address).send(),
    queryKey: ['get-balance', chain, address],
  })
}

Kit の RPC 呼び出しは遅延実行です。client.rpc.someMethod(...) でリクエストを作成し、.send() で実行します。 .send() を忘れてもエラーは発生せず、データが届かないだけです。

残高は bigint の lamport 値として返されます。意図的に変換し、parseFloat は決して使わないでください。

export function lamportsToSol(lamports: bigint) {
  return Number(lamports) / 1e9
}

トランザクションの送信

インストラクションを構築して渡します。ほとんどの場合はこれで対応できます。

import { getAddMemoInstruction } from '@solana-program/memo'
import type { Instruction } from '@solana/kit'

const { sendTransactions } = useMobileWallet()

const instructions: Instruction[] = [getAddMemoInstruction({ memo: 'gm' })]
const signature = await sendTransactions(instructions)

sendTransactions はブロックハッシュ、minContextSlot、手数料支払いアカウント、署名のデコードを処理します。ウォレットが送信した時点で戻るため、トランザクションが完了したとみなす前に署名を確認してください。そのためのヘルパーは references/kit.md にあります。

手数料支払いアカウントの制御、特定のブロックハッシュの有効期間、または手数料の事前確認が必要な場合に限り、明示的な pipe 形式を使ってください。残高と手数料の比較アサーションおよび署名のデコードを含む、実用的な完全例は references/kit.md を参照してください。

参考資料

  • references/kit.md — kit スタック:クラスターと設定、チェーンデータの読み取り、 トランザクション、Solana でのサインイン、メッセージ署名、エラー処理
  • references/web3js.md — 旧 @solana/web3.js スタックと移行の概要
  • references/troubleshooting.md — 原因が判明している接続と署名の失敗

ここで不明な点がある場合は、テンプレートを確認してください。このスキルのパターンは expo-kit-minimal に沿っています。これは完全に動作するアプリであり、文章による説明とは違って最新の状態が保たれています。

npx solana-mobile@latest create /tmp/reference-app --template expo-kit-minimal --skip-install

ウォレットとアプリのどちらに問題があるかを確認する

接続や署名のコードをデバッグする前に、アプリを介さずに同じフローを実行してください。

npx solana-mobile@latest device install fakewallet   # if the device has no wallet
npx solana-mobile@latest playground

playground はデバイス上でウォレットのテストページを表示し、各 MWA インタラクション(接続、サインイン、メッセージへの署名、トランザクションへの署名、署名して送信)をターミナルにストリーミングします。そこでの実行にも失敗する場合、問題はウォレットまたはデバイスにあり、このコードにはありません。solana-mobile スキルでは、両方のコマンドについて説明しています。

関連スキル

  • solana-mobile — プロジェクトのセットアップ、テンプレート、エミュレーター、開発ビルド
  • integration-privy — このウォレット接続に Privy アカウントとセッションを追加する
  • seeker-genesis-token — 接続後に Seeker デバイスの所有権を検証する
  • seeker-domains — 生のアドレスの代わりに .skr 名を表示する

リンク

コメント