news 2026/9/17 8:42:19

Wagmi Connector 开发全指南:从零创建并上架一个钱包连接器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Wagmi Connector 开发全指南:从零创建并上架一个钱包连接器

Wagmi Connector 开发全指南:从零创建并上架一个钱包连接器

【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

本篇指南以 Wagmi 仓库中的 creating-connectors.md 为核心脉络,系统讲解在 Wagmi 中新建、测试并向上游贡献一个 Connector 的完整流程。你将掌握createConnector工厂函数的完整契约(属性、方法、事件与配置参数)、连接器的文件组织与导出规范、测试与文档要求,以及从 changeset 到 Pull Request 的提交路径,可直接套用于packages/connectors的实际开发。

写在动手之前:先沟通,再编码

Wagmi 是一个依赖赞助与赠款维持运转的开源项目,维护第三方 Connector 需要持续投入时间与精力。因此文档在开篇就明确两点前置要求:

  1. 团队需要赞助 Wagmi(wevm 赞助页),这是 Connector 请求被接受的前提之一;如有疑问可邮件联系dev@wevm.dev
  2. 动手写代码之前,先创建 Connector Request 讨论(在 GitHub Discussions 的connector-request分类下发起),说明它解决的是对 Wagmi 用户重要或通用的使用场景,并确认能得到 Wagmi 团队与 Connector 维护团队的支持,避免投入大量精力后 Pull Request 被拒绝。

同时,文档明确警告:并非所有 Connector 都会被 Wagmi 接受。判断标准包括用例的重要性、通用性、维护团队的响应能力等,具体理由贯穿本文后续的每一步要求中。

第 1 步:准备本地开发环境

先按照仓库的 贡献指南 搭建开发环境,核心步骤包括:

  • 使用git clone克隆仓库,或使用gh repo clone wevm/wagmi
  • 确保安装node@对应版本(仓库package.jsondevEngines字段声明了运行时版本),并通过corepack enable启用 pnpm;
  • 在仓库根目录执行pnpm install安装依赖(pnpm 会自动链接各 workspace 包并配置 git hooks);
  • 复制并填写.env环境变量,其中包含链分叉 RPC 地址(VITE_MAINNET_FORK_URLVITE_OPTIMISM_FORK_URL)与 WalletConnect 项目 ID(*_WC_PROJECT_ID)等,供开发 playground 与测试套件使用。

开发环境就绪后,运行pnpm dev:nextpnpm dev:reactpnpm dev:vuepnpm dev:nuxt等命令即可启动./playgrounds下的对应 playground 应用,修改任意包源码(如packages/connectors)会自动热更新到 playground,方便在真实 dApp 环境中验证 Connector。

第 2 步:创建 Connector 源文件

packages/connectors/src目录下新建一个以 Connector 命名的文件,例如新增Foo就创建foo.ts。文件名要求camelCase 且尽量简短

从当前仓库的实际文件布局看,packages/connectors/src下已经存在的 Connector 包括baseAccount.tscoinbaseWallet.tsmetaMask.tssafe.tswalletConnect.ts,以及从@wagmi/core@wagmi/core/tempo重导出的injectedmocktempoWallet(见 packages/connectors/src/exports/index.ts)。新文件应与之并列,命名风格保持一致。

第 3 步:用createConnector搭建骨架

所有 Connector 的根基都是@wagmi/core导出的createConnector工厂函数。文档给出的最小骨架如下:

import { createConnector } from '@wagmi/core' export type FooBarBazParameters = {} export function fooBarBaz(parameters: FooBarBazParameters = {}) { return createConnector((config) => ({})) }

注意三点约定:

  • 函数名与文件名一致:文件叫fooBarBaz.ts,导出函数就叫fooBarBaz
  • 导出一个工厂函数:它接收一个参数对象(FooBarBazParameters,默认{}),返回createConnector的调用结果;
  • createConnector接收一个回调:回调以config为参数(内含chainsemitterprovidersstorage等运行时配置,详见下文「Parameters」),返回 Connector 对象本身。

从 packages/core/src/connectors/createConnector.ts 的源码可见,createConnector的实现只是把回调原样返回(一个恒等包装),它的真正价值在于通过CreateConnectorFn类型对整个返回对象做强类型约束:任何缺失的属性都会在编译期以类型错误的形式暴露出来。

写好骨架后,TypeScript 会立刻报出一个类似Type '{}' is missing the following properties...(错误码 2740)的类型错误,提示你缺少哪些属性。这正是下一步要逐个补齐的内容。

第 4 步:补齐 Properties、Methods、Events 与 Parameters

根据CreateConnectorFn的完整定义(packages/core/src/connectors/createConnector.ts),一个完整的 Connector 由四类成员构成,文档逐类列出如下。

Properties(连接器静态属性)

属性必填说明
iconConnector 的可选图标 URL
idConnector 的唯一标识,camelCase 且尽量简短,例如fooBarBaz
name人类可读的名称,例如'Foo Bar Baz'
rdns可选的反向 DNS 标识,用于在启用createConfig#multiInjectedProviderDiscovery时过滤重复的 EIP-6963 注入式 Provider

以仓库中的 metaMask.ts 为真实范例:

return createConnector<Provider, Properties>((config) => ({ id: 'metaMaskSDK', name: 'MetaMask', rdns: ['io.metamask', 'io.metamask.mobile'], type: metaMask.type, // ... }))

可以看到 MetaMask Connector 的rdns字符串数组(源码类型为string | readonly string[]),覆盖桌面与移动端两个 Provider;type属性则用模块级常量metaMask.type = 'metaMask' as const固定。typeCreateConnectorFn返回类型中的只读必填字段,用于标识连接器种类。

Methods(连接器方法)

方法必填说明
connect连接 Connector
disconnect断开 Connector
getAccounts返回 Connector 当前连接的账户列表
getChainId返回 Connector 当前连接的链 ID
getProvider返回底层 Provider 接口,供 Connector 内部各处使用
isAuthorized返回 Connector 之前是否连接过且仍处于已授权状态
setup在 Connector 首次创建时运行的初始化逻辑
switchChain切换 Connector 的当前链

此外CreateConnectorFn还允许可选的getClient,用于返回 viemClient实例。connect的参数对象(CreateConnectorFn源码定义)包含chainIdisReconnectingwithCapabilities三个可选字段,返回值固定为{ accounts, chainId }结构。

Events(内部事件订阅函数)

事件必填说明
onAccountsChanged订阅账户变更
onChainChanged订阅链变更
onConnect订阅连接事件
onDisconnect订阅断开事件
onMessage订阅 Provider 消息

这些回调的职责是把底层 Provider 的事件转发给 Wagmi 的config.emitter,从而同步 Connector 状态与Config。以 metaMask.ts 为例:

async onAccountsChanged(accounts) { config.emitter.emit('change', { accounts: accounts.map((x) => getAddress(x)), }) }, onChainChanged(chain) { const chainId = Number(chain) config.emitter.emit('change', { chainId }) }, async onConnect(connectInfo) { const accounts = await this.getAccounts() if (accounts.length === 0) return const chainId = Number(connectInfo.chainId) config.emitter.emit('connect', { accounts, chainId }) }, async onDisconnect(error) { // 处理 MetaMask 的 code: 1013 特殊错误:等待重连而不是直接断开 if (error && (error as RpcError<1013>).code === 1013) { const provider = await this.getProvider() if (provider && Boolean((await this.getAccounts()).length)) return } config.emitter.emit('disconnect') },

Parameters(createConnector回调注入的配置)

回调收到的config对象(CreateConnectorFn的参数类型,见 createConnector.ts)包含以下字段:

  • chains:用户配置的链列表,类型为readonly [Chain, ...Chain[]](至少一条链)。switchChain中常用它查找目标链并校验是否已配置;
  • emitter:事件发射器,用于把 Connector 状态同步到 WagmiConfig。可用事件共五类(ConnectorEventMap定义于同一源码文件):
    • change:连接的账户或链发生变化({ accounts?, chainId? });
    • connect:Connector 完成连接({ accounts, chainId });
    • disconnect:Connector 断开;
    • error:Connector 收到错误({ error });
    • message:Connector 收到消息({ type, data? });
  • providers:与 Connector 的rdns匹配的已发现 EIP-6963 Provider 列表;若multiInjectedProviderDiscovery被禁用或没有匹配的 Provider,则为空数组;
  • storage:用户可选配置的存储,默认是对localStorage的封装。safe.tsshimDisconnect功能正是利用config.storage?.setItem/getItem/removeItem('safe.disconnected')记录断开标记(见 safe.ts);
  • transports(源码中还有此可选字段):链 ID 到Transport的映射。

文档同时给出两条贯穿实现始终的 tip:

  1. 第三方 SDK 依赖纪律:若 Connector 使用第三方 SDK,它应依赖尽量少(控制 bundle 体积、降低供应链攻击面)、使用尽可能宽松的开源许可(理想为 MIT),且其package.json应声明"sideEffects": false,以最大化 tree-shaking 支持。
  2. 地址一律校验和(checksum):Connector 返回或发出的所有地址值都必须使用 Viem 的getAddress工具做校验和格式化。仓库中几乎所有 Connector 都遵循此约定,例如safe.ts(await provider.request({ method: 'eth_accounts' })).map(getAddress)

第 5 步:导出 Connector

packages/connectors/src/exports/index.ts按字母顺序导出新 Connector:

export { fooBarBaz } from './fooBarBaz.js'

从 index.ts 的实际内容可以看到,现有导出严格按字母序排列(injectedmocktempoWalletbaseAccountcoinbaseWalletmetaMasksafewalletConnect以及version),新增的fooBarBaz应插入到相应位置。

第 6 步:在 playground 中试跑并编写测试

开发过程中可以借助 dev playgrounds 在真实 dApp 环境里验证 Connector 行为(pnpm dev:next/pnpm dev:react/pnpm dev:vue等)。文档对测试的要求是:

  • 理想情况:在connectorName.test.ts中编写真实单测;
  • 最低要求:若单测难以编写,至少创建测试文件并包含"指令式测试"(instruction tests),覆盖以下三类场景:
    • 如何连接 Connector;
    • 如何断开 Connector;
    • 如何切换 Connector 的当前链(如适用)。

测试文件必须写清楚验证所需的一切信息,例如需要安装的软件(浏览器扩展、移动 App 等)、需要交互或部署的智能合约等。

从仓库现状看,packages/connectors/src下的每个 Connector 都配有同名测试文件(metaMask.test.tssafe.test.tswalletConnect.test.ts等)。另外还要更新导出清单测试 packages/connectors/src/exports/index.test.ts——该文件用 Vitest 的toMatchInlineSnapshot断言导出键的完整集合,新增 Connector 后必须同步更新快照,可以手动改,也可以直接运行:

pnpm test:update packages/connectors/src/exports/index.test.ts

第 7 步:把团队成员加入 CODEOWNERS

Connector 必须被及时更新、持续维护,生产环境用户才能放心依赖。Wagmi 核心团队会尽可能协助 Connector 跟上 Wagmi 的破坏性变更,但依赖处理与 issue/discussion 响应是你所在团队的职责——若问题长期无人处理,该 Connector 可能被从 Wagmi 中移除。

为此,需在 .github/CODEOWNERS 中为 Connector 至少登记一名团队成员,格式如下:

/packages/connectors/src/fooBarBaz @tmm @jxom

仓库现有的 CODEOWNERS 就为每个第三方 Connector 登记了对应生态团队,例如:

/packages/connectors/src/metaMask @wenfix @ffmcgee725 @jiexi @adonesky1 @chakra-guy /packages/connectors/src/safe @DaniSomoza @dasanra @mikhailxyz @yagopv /packages/connectors/src/walletConnect @ganchoradkov @glitch-txs @ignaciosantise @tomiir

登记后,涉及该 Connector 的 Pull Request、issue 等都会自动通知到对应维护者。

第 8 步:编写 Connector 文档

Connector 需要配套文档。参考贡献指南的「Writing documentation」一节 启动文档站点(运行pnpm docs:dev),并添加所需页面。Wagmi 文档站位于./site,使用 VitePress 构建;site/shared/connectors/目录下已有baseAccount.mdcoinbaseWallet.mdinjected.mdmetaMask.mdsafe.mdtempoWallet.mdwalletConnect.md等页面可作为新 Connector 文档的参照模板,注意保持文档简洁、使用平实语言。

第 9 步:创建 changeset

功能与测试就绪后,运行以下命令创建 changeset:

pnpm changeset

按文档要求,该 changeset 应是对@wagmi/connectors仓库的一个patch版本变更,描述格式为Added [ConnectorName],例如Added Foo Bar Baz。changeset 决定了发布时的版本号与 CHANGELOG 文案(变更描述用过去时态动词,如Added/Fixed,这是仓库贡献指南明确约定的命名规范)。

第 10 步:提交 Pull Request

一切就绪后,按贡献指南的 Pull Request 提交规范创建 Pull Request,使用祈使语气命名(如Add something)。合并后,Connector 会进入 Wagmi 的未来版本发布。提交后 GitHub 会自动执行 lint、构建与测试,若出现 ❌ 大多说明代码有 bug,请通过 CI 日志排查。

真实 Connector 源码速览:createConnector的三种落地形态

为了让上文的抽象契约更具体,这里快速对照仓库中三个 Connector 的典型实现模式(都可作为你编写新 Connector 的参考模板):

  • metaMask.ts:基于@metamask/connect-evmSDK 的完整实现。展示了"SDK 实例懒加载 + 动态 import"模式(getInstance()内部使用await import('@metamask/connect-evm')并按需创建客户端)、错误归一化(把UserRejectedRequestErrorResourceUnavailableRpcError映射为 viem 的错误类型)、switchChain中利用addEthereumChainParameterconfig.chains拼装wallet_switchEthereumChain所需的链配置,以及isAuthorized中使用withRetry/withTimeout处理移动端 Provider 在页面加载时的 JSON-RPC 响应延迟。
  • safe.ts:展示了storageItem泛型的使用——createConnector<Provider, Properties, StorageItem>中的StorageItem = { 'safe.disconnected': true }为存储键提供了类型安全;同时演示了shimDisconnect通过config.storage记录断开标记、switchChain可省略(Safe 智能合约钱包仅存在于单链)、以及通过window.parent !== window判断 iframe 环境(Safe App 必须运行在 iframe 中)的细节。
  • walletConnect.ts:基于@walletconnect/ethereum-provider,其参数类型通过Omit<EthereumProviderOptions, ...>精确剪裁 SDK 暴露面,并额外定义了isNewChainsStale这样的 Wagmi 专属语义选项,展示 Connector 如何在 SDK 之上封装 dApp 友好的配置接口。

这三个文件连同各自的*.test.ts测试与site/shared/connectors/下的文档页,构成了一份"可运行的 Connector 参考实现集"。按照本文的十步流程,结合这些范例,你就能完整走通从想法到上游合入的 Connector 开发全链路。

【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 8:41:39

COMSOL多物理场仿真在超声辅助解冻中的应用与优化

1. 项目概述&#xff1a;当三文鱼刺身遇上COMSOL仿真那天实验室的冰箱突然罢工&#xff0c;我精心准备的三文鱼样品在-20℃到4℃的过渡区停留了整整两小时。就在我手忙脚乱抢救样本时&#xff0c;电脑屏幕上COMSOL的仿真曲线突然开始跳探戈——热传导系数和相变潜热的参数设置居…

作者头像 李华
网站建设 2026/9/17 8:40:05

VS Code MinGW头文件路径配置全指南

1. 这个报错到底在说什么&#xff1f;——从编译器视角看懂“检测到 #include 错误”你刚在 VS Code 里敲完#include <stdio.h>&#xff0c;还没点运行&#xff0c;编辑器就在头文件那行底下画了一条鲜红的波浪线&#xff0c;鼠标悬停弹出提示&#xff1a;“检测到 #incl…

作者头像 李华
网站建设 2026/9/17 8:38:41

UBSAN实战:用未定义行为检测器揪出C/C++的隐蔽bug

我见过不少项目在正式发布前跑得好好的&#xff0c;一换编译器版本、一开优化等级就出诡异问题&#xff1a;数组偶尔越界、整数溢出后逻辑跑偏、除法的除数是零却只在极端输入下触发。这种问题最难定位&#xff0c;因为不是每次运行都崩&#xff0c;一旦崩了又很难复现。UBSAN …

作者头像 李华
网站建设 2026/9/17 8:37:52

SpringBoot智慧停车系统设计与实现

1. 智慧停车系统设计背景与核心价值停车难问题已经成为现代城市发展的痛点。根据我多年参与智慧城市项目开发的经验&#xff0c;传统停车管理存在三大核心问题&#xff1a;车位信息孤岛导致资源浪费、停车导航缺失造成时间损耗、人工管理效率低下。这个基于SpringBoot的智慧停车…

作者头像 李华
网站建设 2026/9/17 8:36:01

以太网温湿度传感器通信中CRC16与CRC32选型实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华