浏览器插件钱包(三) - 创建钱包

浏览器插件钱包(三) - 创建钱包

文中的代码仅为片段,具体代码以 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 进行账户创建,接下来我们需要将网络的管理也添加到我们的扩展中去,敬请期待。

下期预告

《浏览器插件钱包(四) - 网络切换》

相关推荐
陪我去看海9 分钟前
被要求猛出小程序,做了这个多平台多环境的部署替我承受压力
前端·微信小程序·抖音小程序
Frag0ut23 分钟前
告别插件时代:HTML5 视频播放对比 Flash/Silverlight/ActiveX 的性能与技术优势解析
前端·html5·视频播放·flash·activex·流媒体技术·silverlight
用户8186180280024 分钟前
MongoDB文档模型设计——从建模到索引
前端
無名路人1 小时前
小程序点餐页吸顶滚动之分类按需加载,上划切换
前端·vue.js·微信小程序
竹林8182 小时前
把神经网络塞进一个浏览器标签页:端侧视觉 AI 的工程真相
前端·浏览器
乘风gg2 小时前
别跟风 AI 副业,工程师最该学的是 AI Coding
前端·ai编程·claude
Frag0ut2 小时前
深度解析:Chrome 自启动与后台常驻服务的作用、关闭方法及利弊权衡
前端·chrome
Amos_Web2 小时前
Rspack 源码解析(十五):Loader Runner 与 JS Loader 桥接
前端·rust·前端框架
YIAN2 小时前
从 SSE 流式到结构化输出:LangChain 三大 OutputParser 与 ToolCall 方案全实战
前端·langchain·node.js
YIAN3 小时前
从 SSE 流式原理到 LangChain 结构化输出:打字机效果与 JSON 解析全方案实战
前端·langchain