关于 solana-mobile-wallet
面向 Solana 移动应用开发的技能指南,介绍通过 Mobile Wallet Adapter 和 Wallet UI React Native Kit 连接钱包、读取链上数据及签名交易。内容还涵盖 Kit 与旧版 web3.js 技术栈的选择和常见集成问题。
主要功能
- 01连接和断开移动钱包
- 02读取 Solana 链上数据
- 03签名并发送交易
- 04处理集群配置与查询缓存
- 05排查钱包连接和签名问题
适用场景
- 01在 React Native 应用中添加钱包连接
- 02获取钱包余额并展示链上数据
- 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.mdtroubleshooting.mdweb3js.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 实际返回值:
| 值 | 类型 | 说明 |
|---|---|---|
account | Account | undefined | 未连接时为 undefined,不是 null |
accounts | Account[] | null | 所有已授权的账户 |
connect | () => Promise<Account> | 打开钱包选择器 |
disconnect | () => Promise<void> | |
client | Client | Kit 客户端——使用 client.rpc 发起 RPC 调用 |
chain | SolanaClusterId | 当前集群。将其加入查询键 |
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——签名相同。模板使用复数形式;两种都可以,请遵循项目中已有的
用法。
以下四点容易让人困惑:
- 没有
connected布尔值。 可通过以下方式推导:const connected = !!account。 signAndSendTransaction的第二个参数是minContextSlot。 只传入交易会失败。 从getLatestBlockhash获取 slot,或使用sendTransactions(instructions),它会为你处理这一参数。- Kit Hook 暴露的是
client,不是connection。connection只存在于 web3.js 技术栈中。 - 将
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。
参考资料
- 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名称,而非原始地址
链接
- 钱包 UI:https://wallet-ui.dev
- MWA 文档:https://docs.solanamobile.com/react-native/overview
- Solana Kit:https://www.solanakit.com