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

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

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

下期预告

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

相关推荐
williamdsy几秒前
【Vue PDF】Vue PDF 组件初始不加载 pdfUrl 问题分析与修复
前端·javascript·vue.js·pdf
南囝coding40 分钟前
这个 361K Star 的项目,一定要收藏!
前端·后端·github
我不吃饼干40 分钟前
我给掘金写了一个给用户加标签的功能
前端·javascript·cursor
羚羊角uou1 小时前
【C++】模拟实现map和set
java·前端·c++
90后的晨仔1 小时前
ArkTS 与 Swift 闭包的对比分析
前端·harmonyos
小小小小宇2 小时前
前端用户行为监控
前端
步行cgn2 小时前
Vue 事件修饰符详解
前端·javascript·vue.js
寻月隐君2 小时前
Solana 开发实战:Rust 客户端调用链上程序全流程
后端·rust·web3
vvilkim2 小时前
Flutter 状态管理基础:深入理解 setState 和 InheritedWidget
前端·javascript·flutter
Magnum Lehar2 小时前
wpf3d游戏引擎前端ControlTemplate实现
前端·游戏引擎·wpf