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

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

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

下期预告

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

相关推荐
亿万托福2 分钟前
数字世界的构筑之艺:前端技术栈的浅描与远瞻
前端
用户40812812003812 分钟前
JWT 和 token 区别
前端
盏茶作酒293 分钟前
打造自己的组件库(三)打包及发布
前端·vue.js
单休好_好就好在比双休少一天3 分钟前
Vite打包从12.17M -> 7.95M,速度提升≈51.85%
前端·javascript
yinke小琪3 分钟前
JavaScript DOM内容操作常用方法和XSS注入攻击
前端·javascript
归于尽4 分钟前
从 TodoList 看自定义 Hook 的设计思想
前端
G等你下课4 分钟前
如何优雅地组织业务逻辑?自定义 Hook 全解析
前端·react.js
刘坤155 分钟前
封装axios二方包
前端·http
饮茶三千6 分钟前
几个在开发中起大作用的CSS新特性!
前端·css
mrsk6 分钟前
React useContext 实战指南:打造主题切换功能
前端·react.js·面试