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

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

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

下期预告

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

相关推荐
lvchaoq24 分钟前
页面停留时间过长导致token过期问题
前端
elangyipi12329 分钟前
深入理解前端项目中的 package.json 和 package-lock.json
前端·json
LYFlied41 分钟前
【算法解题模板】-【回溯】----“试错式”问题解决利器
前端·数据结构·算法·leetcode·面试·职场和发展
composurext42 分钟前
录音切片上传
前端·javascript·css
程序员小寒42 分钟前
前端高频面试题:深拷贝和浅拷贝的区别?
前端·javascript·面试
狮子座的男孩1 小时前
html+css基础:07、css2的复合选择器_伪类选择器(概念、动态伪类、结构伪类(核心)、否定伪类、UI伪类、目标伪类、语言伪类)及伪元素选择器
前端·css·经验分享·html·伪类选择器·伪元素选择器·结构伪类
zhougl9961 小时前
Vue 中的 `render` 函数
前端·javascript·vue.js
听风吟丶1 小时前
Spring Boot 自动配置深度解析:原理、实战与源码追踪
前端·bootstrap·html
跟着珅聪学java1 小时前
HTML中设置<select>下拉框默认值的详细教程
开发语言·前端·javascript
IT_陈寒1 小时前
JavaScript 性能优化:5个被低估的V8引擎技巧让你的代码提速50%
前端·人工智能·后端