浏览器插件钱包(三) - 创建钱包
文中的代码仅为片段,具体代码以 github 仓库的为准
前言:紧接上一篇,我们已经完成了DApp与浏览器扩展后端的连接,这篇文章则会展示我们的浏览器扩展前端与后端的信息交互。
本系列将分为多次文章展示,敬请期待,也可以到github 仓库地址查看最新的代码更新。
架构图
首先我们需要梳理清楚架构图:

从图上我们可以看到,扩展前后端的调用最主要是使用的代理 + 扩展的消息机制,因为Popup前端是无法直接使用background的代码的,打通了这层调用链,我们就能够使用background的数据和操作函数了,我们这里会将所有的操作函数和数据汇总在WalletController中,当前图示只展示了AccountService的,后期还会追加network,tx等管理服务。
代理具体实现
我们需要在background进行整个扩展钱包的数据管理,我们最开始需要实现的就是account的实现。
在实现完AccountService之后,我们需要一个能够处理来自于Popup的请求的函数,所以我们在background.ts中添加如下代码:
background.ts
ts
async function handleUIRequest(request: { method: string; params: unknown[] }) {
const { method, params } = request;
const walletController = WalletController.getInstance();
if (typeof walletController[method as keyof WalletController] === 'function') {
try {
const result = await (walletController[method as keyof WalletController] as (...args: unknown[]) => unknown)(...params);
return { success: true, data: result };
} catch (error) {
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
}
}
return { success: false, error: `Method ${method} not found on WalletController` };
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// 检查消息是否来自我们的 content script
if (
message &&
typeof message === 'object' &&
message.type === `${MESSAGE_PREFIX}${MessageType.REQUEST}`
) {
const request = message.payload;
if (message.type === MessageType.UI_REQUEST) {
handleUIRequest(message.payload)
.then(sendResponse)
.catch(error => sendResponse({ success: false, error: error.message }));
return true; // 保持消息通道开放以支持异步响应
}
});
然后我们在Popup的前端代码里面使用一个proxy对请求进行转发 WalletContext.ts
ts
const walletControllerProxy = new Proxy(
{},
{
get(_, prop: keyof WalletController) {
return function (...args: unknown[]) {
return new Promise((resolve, reject) => {
sendMessageFromPopup(
{
type: MessageType.UI_REQUEST,
payload: {
method: prop,
params: args,
},
},
(response) => {
if (response.success) {
resolve(response.data);
} else {
reject(new Error(response.error));
}
}
);
});
};
},
}
) as WalletController;
创建钱包
在页面 Register.tx 中,我们修改之前的代码,引入 wallet context,调用 wallet context 的 addAccount函数,然后使用 viem 的 mnemonicToAccount 创建 account,就能够创建一个我们想要的 account,最后刷新 wallet context 的数据,获取到 current account 进行展示,这样我们就创建好一个 account 了。
Register.tsx
tsx
export default function Register() {
...
const { wallet, refresh } = useWallet();
...
const handleCreateAccount = async () => {
if (!password || !confirmPassword) {
toast({
variant: "error",
title: "Error",
description: "Please fill in all fields",
});
return;
}
if (password !== confirmPassword) {
toast({
variant: "error",
title: "Error",
description: "Passwords do not match",
});
return;
}
try {
// Generate a new mnemonic using viem
const newMnemonic = generateMnemonic(english);
setMnemonic(newMnemonic);
const account = mnemonicToAccount(newMnemonic);
await wallet.addAccount({
address: account.address,
mnemonic: newMnemonic,
});
await refresh();
toast({
variant: "success",
title: "Account Created",
description: "Your mnemonic has been generated. Please save it securely!",
});
} catch (error) {
console.log(error);
toast({
variant: "error",
title: "Error",
description: "Failed to generate mnemonic. Please try again.",
});
}
};
}


自此我们已经掌握了 Popup 与 background 的交互,能够通过 Popup 进行账户创建,接下来我们需要将网络的管理也添加到我们的扩展中去,敬请期待。
下期预告
《浏览器插件钱包(四) - 网络切换》