- 区块链
- 前端
- 认证鉴权
【免费下载链接】web3modal
The full stack toolkit to build onchain app UX
本文以仓库中 apps/pay-test-exchange/CHANGELOG.md 为主线,系统梳理 Reown Web3Modal 生态中 AppKit Pay(支付与存款)功能、多链钱包适配器(Stellar、TRON、Bitcoin、Solana、EVM)以及 SIWX 签名体系的版本演进脉络。通过逐条对照仓库源码(packages/pay、packages/adapters、packages/siwx 等),读者将理解:支付流程如何从"单一代币直接转账"升级为"任意代币支付 + 自动兑换桥接 + 交易所出金入金"的完整闭环,Stellar 适配器如何接入 WalletConnect 协议族,以及连接事件、签名弹窗、网络切换等 UX 细节背后的实现原理。
一、CHANGELOG 文档背景:一个支付测试交易所的迭代记录
该 CHANGELOG 隶属于pay-test-exchange应用,它是 AppKit Pay 生态中的一个测试用交易所页面:通过模拟"支付成功 / 触发错误"两种场景,帮助 DApp 开发者验证接入 AppKit Pay 后的完整回调链路。其核心交互见 apps/pay-test-exchange/components/exchange-actions.tsx:页面渲染 Reown 品牌信息后,提供Complete Successfully与Trigger Error两个按钮,分别调用markSessionSuccess/markSessionError更新 Cloudflare KV 存储中的会话状态,再跳转到/success或/error页面。
会话状态机定义在 apps/pay-test-exchange/lib/types.ts:
export type Session = { status: 'pending' | 'success' | 'error' createdAt: string txid?: string }写入逻辑见 apps/pay-test-exchange/app/api/update/route.ts:会话通过 Cloudflare KV(SESSIONID_STORAGE)持久化,默认 TTL 为 72 小时(EXPIRY_SECONDS = 60 * 60 * 72);标记成功时会写入一个模拟txid,供 DApp 端轮询getBuyStatus或getQuoteStatus后展示交易结果。这个"测试交易所"恰好是理解 AppKit Pay 服务端回调用例的缩影——CHANGELOG 中大量条目(token 选择、入金流程、余额校验)都能在 packages/pay 的控制器实现中找到对应。
二、AppKit Pay 核心能力演进:从转账到支付闭环
2.1 支付方式矩阵:钱包直付与交易所出金
AppKit Pay 的支付模型经历了三次大的形态跃迁,全部反映在 CHANGELOG 中:
| 版本 | 能力变化 | 源码佐证 |
|---|---|---|
| 0.1.10 | 允许用户手动编辑从交易所入金的存款金额(PR #5004) | PayController.getPayUrl 中金额以字符串传递并做Number归一化 |
| 0.1.13 | 改进 send 流程设计,为余额不足用户增加充值(top-up)选项(PR #4959) | fetchTokens同时支持 EOA 余额与交易所资产两种来源 |
| 0.1.20 | 任意代币支付:付款方可用任意代币,收款方始终收到其偏好代币(自动兑换+桥接);实现 Reown 与合作方手续费分账(PR #5423) | 引入quote与quoteStatus状态,见PayControllerState |
PayControllerState(packages/pay/src/controllers/PayController.ts)完整定义了这一演进的状态空间:paymentAsset、recipient、amount描述支付请求;exchanges描述可用交易所;quote/quoteStatus/quoteError描述兑换报价;currentPayment以type: 'wallet' | 'exchange'区分两种支付通道。
在 0.1.20 之前,支付是"指定代币、直接转账";之后,系统会先通过getQuote拉取兑换报价(packages/pay/src/utils/ApiUtil.ts),再根据quote.transactionStep决定执行路径:EVM 侧走ConnectionController.sendTransaction发送from/to/data/value,Solana 侧走ConnectionController.writeSolanaTransaction提交instructions。若当前网络与目标资产网络不一致,PayController.onSendTransaction会先调用ChainController.switchActiveNetwork切换网络,再发起交易——这正是 0.1.9 修复"open带 send 参数切换网络不抛错导致状态不一致"的延续。
2.2 支付资产处理的三条路径
packages/pay/src/utils/PaymentUtil.ts 将支付执行收敛为三个函数,与 CHANGELOG 的修复条目一一对应:
processEvmNativePayment:EVM 原生币转账。使用ConnectionController.parseUnits按资产精度换算金额,再经sendTransaction发送value(data: '0x')。对应 0.1.12 修复的"用户拒绝交易时误记SEND_ERROR,改为记录SEND_REJECTED"。processEvmErc20Payment:ERC20 代币转账。通过ConnectionController.writeContract调用 ERC20transfer(recipient, amount),ABI 由ContractUtil.getERC20Abi提供。processSolanaPayment:Solana 转账。传入tokenMint时构造 SPL Token 转账指令;本地(native SOL)与 SPL 代币分派逻辑在 PayController.onTransfer 中由paymentAsset.asset是否为'native'决定。
Solana 路径背后有一段值得关注的修复史(0.1.7 与 0.1.8 均提及):Token-2022 代币转账失败,原因是 send 流程使用了 legacy 转账指令。仓库当前实现 createSPLTokenTransactionKit.ts 中可见保护逻辑:
if (mint.programAddress !== TOKEN_PROGRAM_ADDRESS) { throw new Error('Token-2022 mints are not yet supported') }也就是说,SPL Token 标准程序地址(TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA)之外的 Token-2022 Mint 会被显式拒绝而非产生错误指令;同时该文件用字符串大数运算(toTokenBaseUnits)取代 IEEE-754 浮点乘法,避免0.29 * 100 === 28.999...这类精度陷阱——对应 CHANGELOG 中"高精度大额代币兑换失败"(0.1.21)与"SPL 代币无法发送"(0.1.5)的根治方案。
2.3 交易所入金(deposit from exchange)流程
CHANGELOG 中反复出现"deposit from exchange / fund from exchange"相关条目,这是 AppKit Pay 区别于普通转账的独特能力:用户从中心化交易所向链上地址入金。
- 0.1.5:为 fund from exchange 流程增加代币选择,解决部分交易所不支持特定代币的问题(PR #4890);
- 0.1.8:修复"从交易所入金首次尝试不生效"(PR #4941);将文案从 "deposit from an exchange" 统一为 "deposit from exchange"(PR #4957);
- 0.1.10:允许手动编辑入金金额(PR #5004);
- 0.1.13:为"从交易所入金"视图的代币按钮增加链图标(PR #5078)。
入金流程的 API 侧实现集中在 packages/pay/src/utils/ApiUtil.ts:
getExchanges(RPCreown_getExchanges)拉取可用交易所列表,PayController.fetchExchanges 目前截取前两家以兼容现有 UI;getAssetsForExchange(exchangeId)拉取该交易所支持的资产清单,随后fetchTokensFromExchange逐一代币补齐图标 URL(AssetUtil.getImageByToken),并将 CAIP 地址解析为裸地址;getPayUrl({ exchangeId, asset, amount, recipient })生成交易所付款 URL 与会话 ID(sessionId),PayController.openPayUrl按openInNewTab决定以_blank/_self打开。
quote 驱动的入金是 0.1.20 的进阶形态:fetchQuote拿到报价后,如果存在transferStep,会以transferStep.deposit.receiver作为入金接收地址、transferStep.deposit.amount作为金额,调用generateExchangeUrlForQuote生成交易所链接,并用DIRECT_TRANSFER_REQUEST_ID标识"直接转账式入金"的轮询请求。
2.4 支付结果轮询与客户端 API
packages/pay/src/client.ts 暴露了编程式支付入口:
openPay(options):打开支付弹窗;pay(options, timeoutMs):打开弹窗并返回 Promise,默认5 分钟(PAYMENT_TIMEOUT_MS = 300000)超时;通过订阅currentPayment、error、isPaymentInProgress三个状态键,在SUCCESS/FAILED/ 超时三种情况下 settle;getPayUrl/openPayUrl/getAvailableExchanges/getPayResult/getPayError/getIsPaymentInProgress:headless 模式下的组件化调用;subscribeStateKey:事件监听接口,返回取消订阅函数——这正是 0.1.21 新增的"AppKit 事件监听器 unsubscribe 能力"(PR #5447)的落地。
三、新增 Stellar 适配器:WalletConnect 协议族的新成员(0.1.23)
0.1.23 中最具里程碑意义的一条:通过新的@reown/appkit-adapter-stellar包接入 Stellar 网络,支持stellar:pubnet与stellar:testnet两条网络,以及stellar_signXDR、stellar_signAndSubmitXDR、stellar_signMessage、stellar_signAuthEntry四个签名方法,并附带针对 SEP-53 签名的 SIWX 验证器。Stellar 钱包仅通过 WalletConnect 连接,不支持扩展钱包。
3.1 适配器骨架:扩展钱包的"无操作"实现
packages/adapters/stellar/src/adapter.ts 中,StellarAdapter extends AdapterBlueprint<StellarConnector>,其设计要点:
syncConnectors()被刻意留空——因为 Stellar 没有注入式钱包可发现,WalletConnect 连接器在setUniversalProvider中注册;- 其余 EVM 形态的蓝图方法(
sendTransaction、writeContract、estimateGas、grantPermissions、getCapabilities等)全部以满足接口但不假装做事的方式实现为 no-op。这是因为 Stellar 交易是 DApp 侧构建的 XDR 信封,签名应走连接器的signXDR/signAndSubmitXDR,而非通用 EVM 方法; getBalance直接请求HorizonRPC({horizonUrl}/accounts/{address})读取原生 XLM 余额;对"从未被注资(404)"的账户返回零余额而非报错——注释明确说明 Blockchain API 目前尚无 Stellar 支持。
3.2 连接器:四类 stellar_* 方法的统一路由
packages/adapters/stellar/src/connectors/StellarWalletConnectConnector.ts 实现 Stellar WalletConnect 规范的方法集。核心机制是私有的requestStellarMethod:每个请求都通过ChainController.getCaipNetworkByNamespace(ConstantsUtil.CHAIN.STELLAR)取当前激活网络,并把chain与account(形如stellar:pubnet:ADDRESS)附加到 payload 上:
return await this.provider.request<T>( { method, params: { ...rest, chain: chain.caipNetworkId, account: `${chain.caipNetworkId}:${address}` } }, chain.caipNetworkId )这把网络解析绑定到会话链 ID 而非调用方传入的参数——钱包据此路由到正确的密钥。signAndSubmitXDR会把钱包返回的tx_hash映射为txHash,并仅在调用方提供时才透传waitForInclusion。配套测试 packages/adapters/stellar/tests/connectors/StellarWalletConnectConnector.test.ts 逐一验证了四个方法的请求形状与stellar:pubnet网络绑定。
3.3 SIWX 验证器:SEP-53 域分隔签名
packages/siwx/src/verifiers/StellarVerifier.ts 是配套的会话验证器。Stellar 与 Solana 一样使用 Ed25519 签名,但钱包签署的是 SEP-53 规定的域分隔载荷sha256("StellarMessage" || 0x00 || message),而非消息本身;地址使用 StrKey 编码而非 base58。验证流程:
const payload = new Uint8Array(prefix.length + 1 + message.length) payload.set(prefix, 0) payload[prefix.length] = 0 payload.set(message, prefix.length + 1) const digest = sha256(payload, 'bytes') return nacl.sign.detached.verify(digest, signature, publicKey)该验证器已默认注册进 packages/siwx/src/configs/DefaultSIWX.ts 的默认配置(new StellarVerifier()),与 EIP155、Solana、BIP122、TRON 验证器并列。
四、SIWX 签名与连接恢复的修复纵深
4.1 SIWX 签名弹窗不出现的修复(0.1.23)
CHANGELOG 0.1.23 修复了"连接后 SIWX 签名弹窗不出现,需刷新页面才生效"的问题,复现场景覆盖 Solana 适配器 + 邮箱登录、Farcaster 登录以及浏览器注入钱包连接。根因在于 packages/siwx/src/signers/DefaultSigner.ts 的签名链路依赖实时状态:signMessage依次从ChainController取当前网络、从AdapterController取适配器、从ChainController.getAccountData取地址、从ConnectorController取连接器 ID,任一环节在连接事件完成前未就绪,签名就无法发起。该文件还包含一个细节:当连接器是AUTH(邮箱/社交登录)时,会先RouterController.pushTransactionStack({})压入交易栈,为弹窗导航做准备。
4.2 多链钱包的签名竞态修复(0.1.23, PR #5736)
Trust Wallet、SafePal 等多链钱包的 SIWX 签名在"消息创建与签名之间活跃 namespace 发生变化"时会失败。从架构上看,SIWX 的签名委派链是SIWXConfig.signMessage → signer.signMessage(message, chainId)(packages/siwx/src/core/SIWXConfig.ts),而DefaultSigner在未显式传chainId时回退到ChainController.getActiveCaipNetwork()——这就是竞态窗口:若用户在弹窗中切换了网络,签名请求携带的 namespace 与消息创建时不一致,适配器便找不到对应签名者。该修复思路与 0.1.4 修复的"取消 SIWX 签名未恢复原网络状态"一脉相承,都是围绕 namespace 一致性问题做状态收敛。
4.3 TRON 连接在重载时被静默清空(0.1.23, PR #5746)
这是 0.1.23 中另一个教科书级的引导同步(boot sync)竞态修复:当 TronLink 等钱包适配器在引导同步运行时仍处于readyState解析中,已存储的 TRON 连接会被静默抹掉。修复方式体现在 packages/adapters/tron/src/adapter.ts 的syncConnectors与 packages/adapters/tron/src/utils/TronConnectUtil.ts 中:
syncConnectors先启动watchWalletAdapters(监听readyStateChanged事件),再await waitForLoadingAdapters(...);waitForLoadingAdapters通过withRetry轮询(间隔 200ms、上限 3000ms),但只等待存在实际存储的 TRON 连接(hasConnected && !hasDisconnected)且处于Loading的适配器——未安装的钱包或仅通过 WalletConnect 连接的会话不会阻塞引导,从而避免了"每次页面加载都延迟数秒"的副作用;- 配套测试 packages/adapters/tron/src/tests/adapter.test.ts 断言
syncConnectors在解析前确实 await 了waitForLoadingAdapters。
TronConnectUtil还包含一个"防冒名"校验:isGenuineAdapter通过检查window.tron.isTronLink === true或window.tronLink.ready === true,过滤掉为兼容性注入window.tronLink的仿冒钱包(如 Klever),避免 TronLinkAdapter 误报 "Found"。
五、连接、账户与钱包发现层的 UX 修复
5.1 Coinbase 连接器偏好回归修复(0.1.22)
0.1.22 修复了coinbasePreference配置被忽略的回归(源自 PR #5269):'all'与'eoaOnly'现在正确使用coinbaseWallet连接器(含 QR 码支持),'smartWalletOnly'使用baseAccount。底层实现见 packages/adapters/wagmi/src/client.ts 的addThirdPartyConnectors:从OptionsController.state读取enableCoinbase/enableBaseAccount/coinbasePreference,分别通过getBaseAccountConnector与getCoinbaseConnector(this.wagmiConfig.connectors, coinbasePreference)组装连接器。测试 packages/adapters/wagmi/src/tests/client.test.ts 对三种偏好分支(all/eoaOnly/smartWalletOnly)均有断言。
5.2 事件语义与钱包排名(0.1.11 / 0.1.10 / 0.1.12)
事件体系的演进体现了产品对"失败原因"的精细化管理:
- 0.1.10:用户拒绝连接不再记
CONNECT_ERROR,改记新事件USER_REJECTED(PR #4992); - 0.1.11:
CONNECT_SUCCESS事件新增walletRank属性,并创建@appkit/recent_walletlocalStorage 键记录最近连接的 wallet(PR #5030); - 0.1.12:用户拒绝交易不再记
SEND_ERROR,改记SEND_REJECTED(PR #5021)。
这些事件常量与载荷定义在 packages/controllers/src/utils/TypeUtil.ts(CONNECT_SUCCESS、USER_REJECTED、DISCONNECT_SUCCESS等),而walletRank的取值来自连接器在钱包榜单中的排序connector?.explorerWallet?.order,见 packages/controllers/src/controllers/ConnectionController.ts。结合 packages/controllers/src/controllers/ApiController.ts 中的walletRanks预取,可以推断walletRank用于在事件分析中衡量钱包发现效率。
5.3 钱包发现与展示的细节修正
- 0.1.18:改进钱包搜索,按平台与 SDK 类型过滤掉不匹配的钱包(PR #5296)——对应 ApiController 的过滤逻辑在 0.1.8 中已允许 webapp 钱包在移动端出现;
- 0.1.19:修复 ProfileWallets 标签页顺序(PR #5378),以及应用内钱包浏览器中钱包按钮失效(PR #5364);
- 0.1.21:修复折叠钱包按钮显示复制图标而非钱包图标(PR #5450)、iOS 设备勾选图标不显示(PR #5449);
- 0.1.23:
wui-button作为 flex 子项可收缩,标签以省略号截断,避免 Sign 弹窗的 Cancel/Sign 按钮在同时处于 loading 时溢出弹窗边界(PR #5758)。该样式位于 packages/ui/src/composites/wui-button/styles.ts:button设white-space: nowrap,内部wui-text设min-width: 0; overflow: hidden; text-overflow: ellipsis,图标与 spinner 设flex-shrink: 0——正是"按钮可收缩 + 文本省略号截断"的实现。弹窗消费方见 packages/scaffold-ui/src/views/w3m-siwx-sign-message-view/index.ts,两个wui-button(neutral-secondary的 Cancel 与neutral-primary的 Sign)在isCancelling/isSigning时为?loading并切换文案 "Cancelling..." / "Signing..."。
六、转账、余额与网络细节:Bitcoin 与 Solana 专项
- 0.1.2:修复 WalletConnect 连接在
CONNECT_SUCCESS事件中缺少address(PR #4744);Bitcoin 连接新增 Binance Web3 Wallet deeplink 支持(PR #4748); - 0.1.4:引入连接钱包时的校验和地址(checksum)转换(PR #4821);修复移动钱包拒绝连接后无法返回上一屏重新发起(PR #4819);
- 0.1.5:修复 Bitcoin WalletConnect 签名请求不含正确用户地址(PR #4886);修复 SPL Solana 代币无法通过 send 流程发送(PR #4877);
- 0.1.6:新增 Bitcoinsignet网络(PR #4891);修复邮箱与社交登录禁用时
authConnector仍被注入 wagmi 配置(PR #4892); - 0.1.12:修复 OKX Bitcoin 钱包连接时
publicKey返回undefined(PR #5048); - 0.1.13:修复通用提供器在钱包拒绝连接时抛出随机对象错误(PR #5079);
- 0.1.21:修复 Bitcoin 钱包返回的账户缺少 purpose 类型(PR #5452);Solana 转账失败时展示非友好错误消息的问题(PR #5444)。
这些条目大多对应 packages/adapters/bitcoin、packages/adapters/solana 与 packages/adapters/wagmi 的控制器/连接器实现,其中 "purpose 类型" 与 Bitcoin 多签名(multisig)账户体系相关,"publicKey 缺失"则与 OKX 注入式 Bitcoin 钱包的账户枚举实现相关。
七、网络标识与资产展示的细节
- 0.1.9:修复 SIWX 屏幕钱包图标在 Safari 上渲染异常(PR #4965)、头部问号图标显示异常(PR #4982)、send 输入框缺少数字格式校验(PR #4968),并新增
open函数的send 参数(token、amount、chain、recipient 预配置,PR #4962); - 0.1.14:新增
getDisabledCaipNetworksAPI 用于获取被禁用的 CAIP 网络(PR #5098); - 0.1.16:AppKit 新增用量上限 UI,超限时展示(PR #5225);
- 0.1.23:修复带损坏图片 URL 的代币显示裂图(PR #5747);修复 Rootstock 网络 logo 未在网络选择器中显示(PR #5723);Sign In 消息请求中默认钱包图标加大(PR #5755);"Don't have X?" 下载 CTA 不再硬截断钱包全名(PR #5756)。
其中 send 参数预配置能力与 packages/pay/src/client.ts 的openPay(options)互为表里:PaymentOptions中的paymentAsset(含network、asset、metadata)、recipient、amount会被写入PayController状态,并在handleOpenPay中通过PAY_MODAL_OPEN事件上报配置快照。getDisabledCaipNetworks则可从 packages/controllers/src/controllers/ChainController.ts 的网络状态中推断,用于在 UI 层灰显不可用网络。
八、版本维护与依赖治理
CHANGELOG 也记录了工程化层面的例行升级:
- 0.1.14 / 0.1.16 / 0.1.23:多次将 viem、wagmi、WalletConnect、Solana 等依赖升级到最新版本;0.1.23 明确"Upgraded viem to latest version";
- 0.1.16:修复打包时包版本注入问题(PR #5247)——对应仓库根目录 scripts/inject-version.js 的职责;
- 0.1.17:修复使用 appkit core 时仍调用远程配置端点的问题(PR #5274);
- 0.1.3:将
ReownAuthentication类迁入@reown/appkit-controllers包,并通过 AppKit features 与 remote features 简化集成(PR #4750)——这是认证能力从应用层下沉到控制器层的关键重构。
pay-test-exchange应用本身基于 Next.js 15(Turbopack dev,端口 4001)与 OpenNext Cloudflare 部署,部署脚本opennextjs-cloudflare build && opennextjs-cloudflare deploy见 apps/pay-test-exchange/package.json。
九、总结:从 CHANGELOG 看 AppKit Pay 的设计哲学
回看 apps/pay-test-exchange/CHANGELOG.md 从 0.1.1 到 0.1.23 的 23 个补丁版本,可以提炼出三条清晰的设计主线:
- 支付通道的多元化:从"钱包直接转账"单一通道,演进为"钱包直付 + 交易所出金 + 报价驱动的自动兑换桥接"三条通道并存,
PayController的状态机与quote体系是这一演进的载体; - 多链适配器的可插拔范式:
AdapterBlueprint抽象让 Stellar(纯 WalletConnect)、TRON(扩展 + WalletConnect 混合)、Solana、Bitcoin 等异构链以统一接口接入,扩展钱包不存在的链用显式 no-op 满足接口而非硬造实现; - 状态一致性的执念:从 SIWX 竞态、TRON 引导同步、钱包切换后的 stale provider,到 Cancel/Sign 按钮溢出,绝大多数 Patch 都在对抗"异步状态在何时以何种顺序收敛"这一核心复杂度。
对于正在接入 AppKit Pay 或扩展多链支持的开发者,这份 CHANGELOG 既是"已知问题与规避方式"的索引,也是理解 packages/pay、packages/adapters、packages/siwx 源码架构的最佳导读。
- 区块链
- 前端
- 认证鉴权
【免费下载链接】web3modal
The full stack toolkit to build onchain app UX
相关推荐
Adobe-GenP 3.0破解工具:5分钟解锁Adobe全系列创意软件的完整指南
Adobe GenP 3.0破解工具:5分钟解锁Adobe全系列创意软件的完整指南 Adobe Creative Cloud是创意工作者的必备工具,但高昂的订阅
区块链前端认证鉴权Apify MCP Server 版本演进深度解读:从 0.1.1 到 0.16.1 的完整 CHANGELOG 指南
Apify MCP Server 版本演进深度解读:从 0.1.1 到 0.16.1 的完整 CHANGELOG 指南 本文以仓库根目录的 CHANGELOG.
如何免费部署 wvp-GB28181-pro:3步跑通 GB28181 视频监控平台
如何免费部署 wvp GB28181 pro:3步跑通 GB28181 视频监控平台 wvp GB28181 pro 是一个 GB28181 视频监控平台,可接
后端音视频前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考