移动/游戏
在 Solana Expo Android 应用上叠加…
当 Solana 移动端应用需要跨设备的稳定用户身份和服务器可验证会话,而签名仍由钱包完成时,用 Privy 的 Sign-In-With-Solana 在 MWA 之上叠加认证。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Solana Expo Androi特定
解决思路
- 先在 https://dashboard.privy.io 创建 Privy 应用:登录后点击 New app,命名并选择 Mobile app,保存 App ID。
- 在 User management > Authentication 的 External wallets 卡片中启用 SVM (Solana) wallets——这个开关很容易漏掉,关闭时即使钱包签名正确,login 也会拒绝所有 SIWS 尝试。
- 在 App settings > Clients 中把 app identifier 设为 app.json 里的 expo.android.package 值,保存 Client ID——Privy 会校验调用应用的包名。
- 把 EXPO_PUBLIC_PRIVY_APP_ID 和 EXPO_PUBLIC_PRIVY_CLIENT_ID 写入环境变量;两者都是公开的客户端标识符,所以用 EXPO_PUBLIC_ 前缀是正确的。
- 绝不要把 Privy app secret 放进移动应用——任何 EXPO_PUBLIC_ 前缀的内容在发布包中都可读,secret 只能用于服务端代码。
- 安装依赖:npx expo install @privy-io/expo @privy-io/expo-native-extensions;@privy-io/expo 的 peer dependency 列表很长且随版本变化,按所选版本要求安装,不要照抄任何清单。
- 完成三处原生接线:在入口模块最先加载 crypto 和 text-encoding polyfill;在 app.json plugins 中加入 expo-secure-store 和 expo-web-browser;加 Metro resolver 覆盖让 jose 解析到浏览器构建。
- 接线后重新原生构建(npx expo run:android)——JS reload 不会加载新的原生模块。
- 挂载 providers:用 QueryClientProvider 包住 PrivyProvider,PrivyProvider 再包住 MobileWalletProvider;PrivyProvider 与 MobileWalletProvider 互不依赖,嵌套顺序随意,但都必须位于所有屏幕之上,QueryClientProvider 在两者之上。
- 在缺少环境变量时主动抛错——undefined 值会以畸形 app ID 传给 Privy,并在很久之后才以不透明的初始化错误暴露。
- 给 PrivyProvider 传入 clientId:虽然 PrivyProviderProps 中它被标为可选,但 Privy 移动端文档视其为必需,且 dashboard 会为每个移动客户端签发一个。
- 用 usePrivy() 时先等待 isReady 为 true 再渲染任何登录态:isReady 为 false 时渲染未登录界面会让已认证用户在每次冷启动时闪现登录页。
- 处理 usePrivy() 的返回值:user 未认证时为 null(不是 undefined),error 表示初始化失败(通常是存储访问),getAccessToken 每次请求时调用且绝不缓存结果。
- 用 useLoginWithSiws() 实现登录三步:generateMessage 生成消息、signMessages 签名、login 交换会话。
- 调用登录前必须已连接钱包(useMobileWallet().account 已定义),否则 signMessages 会触发自己的授权流程。
- generateMessage 时传 account.address(base58),不要传 account.addressBase64——后者是 MWA 的线格式,Privy 不接受。
- 用 new TextEncoder().encode(message) 编码消息后交给 signMessages,再用 fromUint8Array(signedPayload) 得到 base64 字符串传给 login——Privy 要 base64,base58 会验证失败。
- 不要截断签名字节:signMessages 返回的是 MWA 的 signed payload,不是裸的 64 字节签名,整体 base64 编码后交给 Privy。
- from.domain 用 RFC 3986 authority(纯主机名,无 scheme 无路径),from.uri 用完整 URI(通常是应用 deep link),两者保持稳定,因为它们会嵌入用户在钱包中看到的签名消息。
- 登出时同时调用 await logout() 和 await disconnect():只 disconnect 会留下无钱包的活跃 Privy 会话,只 logout 会留下已授权钱包并在下次尝试时静默重新登录。
- 明确职责边界:私钥与签名归钱包应用(经 MWA),连接地址用 useMobileWallet().account,跨设备用户身份用 usePrivy().user,服务器可验证会话用 usePrivy().getAccessToken(),发送交易用 useMobileWallet().sendTransactions。
- UI 必须处理「已连接但未登录」等所有组合状态,登录按钮就属于这个状态。
- 遇到歧义时参考完整可运行模板:npx solana-mobile@latest create /tmp/reference-app --template expo-kit-privy --skip-install。
适用边界 · 注意事项
- 仅需要获取已连接地址的应用不需要 Privy,单独使用 solana-mobile-wallet 技能即可。
- 仅支持 Android 且必须使用 development build:MWA 不支持 iOS,也无法在 Expo Go 中运行。
- 如果只需要服务端验证 SIWS 而不需要 JWT,用 seeker-genesis-token 技能,Privy 属于过度设计。