关于 frontend-apps
面向构建 Sui 前端 dApp 的开发者,提供基于当前 dApp Kit 的实现与迁移指南,涵盖 React、Vue 及其他非 React 框架,并强调避免过时 API 和常见交易问题。
主要功能
- 01dApp Kit 安装与初始化
- 02React 钱包连接与状态管理
- 03Vue、Svelte 和原生 JS 模式
- 04TanStack Query 链上数据查询
- 05交易签名、执行与状态检查
- 06旧版 API 迁移及代码审查
适用场景
- 01搭建新的 Sui React dApp
- 02实现 Vue 或 Svelte 钱包界面
- 03查询余额并提交链上交易
- 04检查旧教程代码并排查过时 API
MCP 相关
建议在可用时查询 Sui 文档 MCP 服务,以核实最新信息并补充参考文件未涵盖的细节。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mystenlabs/ skills --skill frontend-apps安装后在 Claude Code 中输入/frontend-apps确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
limitations.mdnon-react.mdqueries.mdreact.mdsetup.mdtransactions.mdevals/code-evals.jsonevals.json详细介绍
Sui 前端 / dApp 工具包
**MCP 工具:**如果你的环境支持,请同时查询 Sui 文档 MCP 服务(
https://sui.mcp.kapa.ai)以获取最新答案。请用它核验信息,并了解这些参考文件未涵盖的细节。
浏览器中的 Sui 应用常因以下原因出错:
- 使用了错误的包。
@mysten/dapp-kit(不带后缀)是仅支持旧版 JSON-RPC 的包——已弃用。新代码使用@mysten/dapp-kit-react或@mysten/dapp-kit-core。 - **使用了错误的客户端。**dApp Kit 在
createDAppKit的createClient中使用SuiGrpcClient(推荐)。不要传入SuiJsonRpcClient——JSON-RPC 已弃用。 - **使用了旧版 Provider 堆栈。**代码经常尝试使用 v1 模式:
QueryClientProvider→SuiClientProvider→WalletProvider。这种模式已淘汰。新模式:createDAppKit工厂 +DAppKitProvider(或非 React 等效方案)。 - 使用了已移除的钩子。
useSuiClientQuery、useSuiClientInfiniteQuery、useSignAndExecuteTransaction(mutation 钩子)、useConnectWallet、useDisconnectWallet、useSuiClient、useSuiClientContext——均已移除。现在使用useCurrentClient/useCurrentNetwork/useDAppKit()(命令式方法)+ 自行封装的 TanStack Query。 - **在执行交易和重新获取数据之间跳过
waitForTransaction。**全节点会异步索引交易——在signAndExecuteTransaction后立即使 TanStack 缓存失效,会重新获取过期数据。 - **在应用代码中调用
tx.build()构建 PTB,再交给钱包。**这会妨碍钱包选择 Gas。始终将Transaction实例传给钱包。
此技能中的所有模式均基于以下资料:
- https://sdk.mystenlabs.com/dapp-kit(首页)
- https://sdk.mystenlabs.com/dapp-kit/getting-started/react
- https://sdk.mystenlabs.com/dapp-kit/getting-started/next-js
- https://sdk.mystenlabs.com/dapp-kit/getting-started/vue
- https://docs.sui.io/standards/wallet-standard
如果不确定某个 API,请从相关页面获取信息——不要根据旧版 @mysten/dapp-kit 或 v2 之前的钩子接口进行推断。网上有很多过时的教程。
参考文件
setup — 安装、工厂、Provider
路径: setup.md
**适用场景:**开始新的 dApp 项目、搭建 React/Next.js/Vue 项目,或从旧的三 Provider 模式迁移时。涵盖包的选择、createDAppKit({ networks, createClient })、DAppKitProvider(React)以及 declare module TypeScript 扩展。
react — React 钩子与模式
路径: react.md
**适用场景:**编写 React 组件时。涵盖所有当前钩子(useCurrentAccount、useCurrentWallet、useCurrentNetwork、useCurrentClient、useDAppKit、useWallets、useWalletConnection)、标准组件(ConnectButton)以及需要连接钱包才能使用的界面惯用模式。
non-react — Vue、原生 JS、Web Components
路径: non-react.md
**适用场景:**构建 Vue / Svelte / 原生 JS dApp 时。涵盖 @mysten/dapp-kit-core、nanostores 响应式状态(如 dAppKit.stores.$connection)、Web Components 注册,以及通过 @nanostores/vue 绑定到 Vue。
queries — TanStack Query 模式
路径: queries.md
**适用场景:**在 dApp 中获取链上数据(余额、自有对象、代币、动态字段、交易)时。涵盖 useQuery + useCurrentClient、用于分页结果的 useInfiniteQuery、enabled 条件判断,以及写入后的缓存失效。
transactions — 签名与执行
路径: transactions.md
**适用场景:**从 dApp 提交任何交易时。涵盖 dAppKit.signAndExecuteTransaction、基于 $kind 的结果判别、signTransaction(钱包只签名、不执行——用于赞助交易流程)、signPersonalMessage、waitForTransaction + invalidateQueries 顺序,以及常见的钱包 UX 故障。
限制——前端不能做或不应做的事
路径: limitations.md
在以下情况下加载: 用户正在设计的功能似乎会越过浏览器环境的边界。涵盖:仅限后端的功能(加油站内部信息、验证者密钥)、浏览器/SSR 注意事项、自动连接的可靠性,以及“不要在浏览器中放置机密信息”的规则。
路由指南
| 任务 | 加载内容 |
|---|---|
| 从头开始构建新的 React dApp | setup + react + queries + transactions |
| 构建新的 Vue/原生 JavaScript/Svelte dApp | setup + non-react |
| “如何连接钱包?” | react(或 non-react) |
| “如何查询余额/拥有的对象?” | queries |
| “如何发送交易?” | transactions +(用于构建 PTB 的 sui-sdks) |
| 赞助交易流程 | transactions(前端部分)+ ptbs(PTB 部分) |
从 @mysten/dapp-kit(无后缀)迁移 | setup + limitations + 按需加载其他内容 |
| 为什么我的 UI 在交易后没有更新? | transactions + queries |
| 处理 SSR/Next.js | setup + limitations |
| 对 dApp 进行完整代码审查 | 所有参考文件 + 以下代码审查清单 |
| “审查这段代码”/“这段代码有什么问题?” | 以下代码审查清单 + 相关参考文件 |
技能内容
关键概念
- 两个包,一个 API。
@mysten/dapp-kit-react封装了@mysten/dapp-kit-core。两者都有createDAppKit;操作(signAndExecuteTransaction、signTransaction、switchNetwork、connectWallet、disconnectWallet)完全相同。不同之处在于如何读取响应式状态:React 使用钩子;非 React 则读取 nanostores 存储。 - 一个实例,多个网络。
createDAppKit({ networks: [...], createClient })会创建一个了解多个网络的 dApp Kit 实例。dAppKit.switchNetwork(name)会切换当前活动网络。createClient工厂会为每个网络延迟调用一次。 - 默认使用 gRPC。 新版 dApp Kit 基于
SuiGrpcClient构建。JSON-RPC 已弃用;请在createClient中使用SuiGrpcClient。 - 钱包仅限浏览器环境。 钱包检测使用
window.navigator.wallets和 CustomEvents。任何涉及钱包状态的组件都必须在客户端渲染。在 Next.js/SSR 框架中,这意味着要在涉及钱包的组件上添加'use client'。 - Gas 由钱包负责。 应用构建
Transaction并将其传递给钱包。钱包会选择 Gas 币、设置预算(通过 dry-run)并签名。除非是赞助交易流程,否则绝不要调用tx.build()并传递字节——请参阅transactions.md。 - 全节点的数据最终一致。
signAndExecuteTransaction会在数据可查询之前返回摘要。重新获取数据前,务必先调用waitForTransaction。
规则
- 使用
@mysten/dapp-kit-react或@mysten/dapp-kit-core——新代码中不要使用裸包@mysten/dapp-kit。该包仅支持 JSON-RPC,且已弃用。 - 在
createClient中使用SuiGrpcClient。 不要使用SuiClient——SuiClient已在 v2 中移除;请使用来自@mysten/sui/grpc的SuiGrpcClient。 - 使用
createDAppKit+DAppKitProvider。 不要使用三个 Provider 组成的堆栈(QueryClientProvider+SuiClientProvider+WalletProvider)。如果使用 TanStack Query 获取数据,仍需用QueryClientProvider包裹,但 dApp Kit 本身不需要它。 - 加入
declare moduleTypeScript 扩展声明, 这样 Hooks 无需手动传入实例也能获得正确类型。 - 不要使用已移除的 Hooks。
useSuiClientQuery/useSuiClientInfiniteQuery/useSuiClientContext/useSuiClient/useSignAndExecuteTransaction(mutation Hook)/useConnectWallet/useDisconnectWallet均已移除。请使用useCurrentClient+useQuery/useInfiniteQuery+useDAppKit()命令式方法。 - 检查当前账户是否为
null。 连接前,useCurrentAccount()会返回null。始终使用if (!account) return,或在查询中通过enabled: !!account设置执行条件。 - 使缓存失效前先调用
waitForTransaction。 先执行await client.waitForTransaction({ digest: result.Transaction.digest }),再执行queryClient.invalidateQueries(...)。反过来会获取到过期数据。 - 将
Transaction实例传给钱包,而不是传入await tx.build(...)得到的字节。 钱包需要负责选择 gas。例外:使用tx.build({ client, onlyTransactionKind: true })的赞助交易流程——请参见ptbsskill。 - 检查
result.$kind === 'FailedTransaction'(或result.FailedTransaction)。 不要假定交易成功。不要使用 v1 的result.effects?.status?.status。 - 钱包相关 UI 必须在客户端渲染。 如果 SSR 时没有客户端保护,钱包按钮会在检测到钱包前渲染。请使用
'use client'/ 动态导入 / 基于 effect 的水合。 - Vue:
useStore返回 Vue ref——在脚本代码中使用.value。const connection = useStore(dAppKit.stores.$connection)会返回一个 ref。在<script setup>中,通过connection.value.account访问状态。Vue 会在模板中自动解包 ref,但脚本示例中始终展示.value的用法。
代码审查清单
当用户要求你审查代码片段时,不要只指出最先发现的 2–3 个问题。请逐项检查此清单,并指出所有匹配项。Browser Sui 代码通常在单个组件中堆叠了 5–10 个问题,因为它是从 v1 教程中复制而来的。
导入 / 软件包
@mysten/sui.js(出现在任何位置)——已冻结的 v1 软件包;替换为@mysten/sui。@mysten/dapp-kit(无后缀)——已弃用的仅支持 JSON-RPC 的软件包;替换为@mysten/dapp-kit-react(React)或@mysten/dapp-kit-core(其他框架)。import { ConnectButton } from '@mysten/dapp-kit-react'——路径错误;ConnectButton和ConnectModal从@mysten/dapp-kit-react/ui导出。使用错误路径会导致白屏,且不会显示任何错误。import { SuiClient }——SuiClient已在 v2 中移除——使用@mysten/sui/grpc中的SuiGrpcClient(或在组件内使用useCurrentClient())。import { TransactionBlock }——在 v2 中已重命名为Transaction。
已移除的钩子(出现任何一个都属于错误)
useSuiClientQuery、useSuiClientInfiniteQuery、useSuiClientContext、useSuiClient——替换为useCurrentClient()+ TanStack 的useQuery/useInfiniteQuery。useSignAndExecuteTransaction(变更钩子)——替换为在事件处理程序中命令式调用useDAppKit().signAndExecuteTransaction(...)。useConnectWallet、useDisconnectWallet——替换为useDAppKit().connectWallet()/disconnectWallet()。
Provider 组合
SuiClientProvider+WalletProvider(v1 的三 Provider 组合)——替换为createDAppKit(...)+<DAppKitProvider>。仍可使用 TanStack 的QueryClientProvider。
在组件内构造客户端
- 在组件主体内调用
new SuiClient(...)/new SuiGrpcClient(...)——会导致网络切换失效,并在每次渲染时重新创建客户端。请使用useCurrentClient()。 - 需要已连接钱包的查询缺少
enabled: !!account——会在 owner 未定义时触发查询并报错。
构造交易
tx.pure(value)(未指定类型)——替换为与 Move 类型匹配的类型化辅助方法:tx.pure.u64(n)、tx.pure.address(addr)、tx.pure.string(s)等。- 将交易传给钱包之前先调用
tx.build()——这会使钱包无法选择 gas。请直接传入Transaction实例。
执行 / 等待 / 状态
signAndExecuteTransactionBlock(...)(v1 方法)——替换为signAndExecuteTransaction(...)。{ transactionBlock: tx }参数形式——替换为{ transaction: tx }。result.effects?.status?.status === 'success'——这是 v1 的形式;替换为result.$kind !== 'FailedTransaction'(或检查result.FailedTransaction)。- 直接访问
result.digest——成功时,digest 位于result.Transaction.digest;失败时没有 digest。 - 执行后立即使缓存失效——必须先
await client.waitForTransaction({ digest }),然后再调用queryClient.invalidateQueries(...)。
SSR / Next.js
- 未添加
'use client'的钱包感知组件——钱包检测会使用仅限浏览器的 API。
完成清单检查后,统计发现的不同问题数量。如果从典型的旧教程多行代码片段中找到的问题少于 5 个,请重新检查代码片段——你几乎肯定漏掉了问题。
常见错误
- 对需要连接钱包的查询使用
@tanstack/react-query的useQuery,却未设置enabled: !!account。 查询会在所有者未定义时触发并报错。 - 从 React-Query 的
queryFn返回Transaction。 交易不是查询;请通过useDAppKit()在变更或事件处理器中使用交易。 - 使用
dAppKit.signAndExecuteTransaction({ transactionBlock: tx })。 新 API 中应使用{ transaction: tx }。 - 直接读取
result.digest。 成功时,摘要位于result.Transaction.digest。失败时没有摘要——请先检查result.FailedTransaction。 - 在
waitForTransaction之前使查询失效。 这是经典的 UI 过时问题。务必先等待。 - 在组件中调用
new SuiGrpcClient(...)。 这会导致网络切换失效。请使用useCurrentClient()。 - 直接实例化
SuinsClient/DeepBookClient。 请在createClient返回的客户端上使用client.$extend(suins(), deepbook({ address }))。 - 保留
autoConnect: true,却未提供清晰的用户体验。 自动重连会在加载时恢复上次使用的钱包。如果应用会处理权限或 nonce,请考虑恢复过程的异步特性(待处理/正在重连状态)。 - 在多个地方硬编码 testnet / mainnet URL。 请将它们集中放在按网络名称索引的
GRPC_URLS映射中,并在createClient中引用。 - 直接使用 wallet-standard API。 dApp Kit 封装了 wallet standard——请勿自行调用
window.navigator.wallets;应使用useWallets()/dAppKit.stores.$wallets。 - 在浏览器中存储私钥或机密。 绝对不要这样做。dApp 通过钱包签名;密钥由后端持有。