mcpField
S
solana-mobile-wallet

solana-mobile-wallet

github.com/solana-mobile开发812.6k 次安装1 周前更新
代码仓库

关于 solana-mobile-wallet

面向 Solana 移动应用开发的技能指南,介绍通过 Mobile Wallet Adapter 和 Wallet UI React Native Kit 连接钱包、读取链上数据及签名交易。内容还涵盖 Kit 与旧版 web3.js 技术栈的选择和常见集成问题。

主要功能

  1. 01连接和断开移动钱包
  2. 02读取 Solana 链上数据
  3. 03签名并发送交易
  4. 04处理集群配置与查询缓存
  5. 05排查钱包连接和签名问题

适用场景

  1. 01在 React Native 应用中添加钱包连接
  2. 02获取钱包余额并展示链上数据
  3. 03构建并发送 Solana 交易

安装

复制下面的命令,在终端中运行。

$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 技能开始。

步骤 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,也不要在同一个应用中混用两者。它们的 provider props、hook 返回值和交易构造方式都不同,因此一方的代码用于另一方时会悄无声息地失效。如果项目完全没有 Solana 客户端,那就是一个新构建——请使用 Kit。

在现有 web3.js 应用中添加新的钱包功能,并不构成在任务中途迁移的理由。请沿用现有方案;如果迁移似乎值得考虑,可将其作为后续事项提及。

步骤 2:确认 provider 已挂载

如果上层没有 MobileWalletProvider,useMobileWallet 会返回空状态。请在根布局或 app-providers 模块中查找它。

请使用 createSolana* 辅助函数来构建集群,而不要手动构建——SolanaCluster 还需要一个 label,辅助函数会自动填入:

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,因为主网没有合适的公开默认值。

provider props 为 cluster、identity,以及可选的 cache、createClient、children。没有 chain prop,也没有 endpoint prop——传入这些参数不会有任何作用。

每个 AppIdentity 字段都是可选的。只需提供 name 即可开始;对于任何正式发布的内容,都应添加真实的深层链接作为 uri,因为钱包会在授权过程中显示该链接,而占位链接可能会被视为钓鱼企图。

请将 @tanstack/react-query 的 QueryClientProvider 放在钱包 provider 上层——下方的 hook 模式使用的是查询和变更操作。

第 3 步:使用 Hook

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

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

kit 技术栈中的 Hook 实际返回值:

值类型说明
accountAccount | undefined未连接时为 undefined,不是 null
accountsAccount[] | null所有已授权的账户
connect() => Promise<Account>打开钱包选择器
disconnect() => Promise<void>
clientClientKit 客户端——使用 client.rpc 发起 RPC 调用
chainSolanaClusterId当前集群。将其加入查询键
sendTransactions(instructions: Instruction[]) => Promise<string>最简便的发送方式
signAndSendTransaction(tx, minContextSlot) => Promise<SignatureBytes>注意第二个参数
signTransaction(tx) => Promise<Transaction>签名但不广播
signMessages(msg: Uint8Array) => Promise<Uint8Array>
signIn(payload) => Promise<SignInOutput>使用 Solana 登录;也可以连接
identity, store配置和授权存储

其中一些方法也提供单数别名——sendTransaction、signMessage、 signTransactions——签名相同。模板使用复数形式;两种都可以,请遵循项目中已有的 用法。

以下四点容易让人困惑:

  1. 没有 connected 布尔值。 可通过以下方式推导:const connected = !!account。
  2. signAndSendTransaction 的第二个参数是 minContextSlot。 只传入交易会失败。 从 getLatestBlockhash 获取 slot,或使用 sendTransactions(instructions),它会为你处理这一参数。
  3. Kit Hook 暴露的是 client,不是 connection。 connection 只存在于 web3.js 技术栈中。
  4. 将 chain 加入每个保存链上数据的 React Query 查询键。 否则切换集群后会 返回前一个网络缓存的余额,看起来像是钱包出了问题。

account.address 是 Kit 的 Address(带品牌标记的字符串),因此可以直接插入文本。 account.label 是钱包提供的名称,可能为 undefined。

连接和断开连接

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 中——取消钱包选择器会使 Promise 被拒绝。 取消是正常情况,不是值得向用户报警的错误状态;请参阅 references/kit.md,了解如何区分取消操作和真正的失败。

授权会被缓存,因此应用重启后无需再次提示即可重新连接。

读取链上数据

使用 Hook 中的 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 lamports 的形式返回。请谨慎转换,切勿使用 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。

参考资料

如果这里的内容有歧义,请阅读模板。本技能中的模式遵循 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 名称,而非原始地址

链接

评论

支持 Markdown