跳到内容

CoinChoice Snap:用任意货币充值 Gas

ETHDenver 黑客马拉松获奖者

11 分钟阅读
CoinChoice Snap:用任意货币充值 Gas

MetaMask Snaps 是让 MetaMask 成为全球最具扩展性钱包的发展路线图。作为开发者,您可以通过全新的方式将您的功能和 API 引入 MetaMask。Web3 开发者是这一增长的核心,本系列旨在展示当今正在构建的创新 MetaMask Snaps。

CoinChoice Snap

Snap 代码库:https://github.com/coinchoice/coinchoice/

您为什么要构建它?

CoinChoice 的诞生源于一个虽小却令人头疼、且极为普遍的问题——Web3 用户可能拥有一个持有有价值代币的钱包,却没有用于执行任何交易的 gas 费用代币。

这一问题对于流动性分散的用户尤为突出。管理多个钱包、在不同区块链生态系统中把握机会的用户,往往需要将 gas 代币分散到每个钱包中,占用了原本用于投资的资金,只为维持数字资产和交易的正常运转。除 gas 管理外,当代币被转入一个用户尚未持有 gas 的网络时,用户必须先获取 gas 才能进行任何后续操作。通常,这意味着用户需要访问中心化交易所来获取并提取 gas,从而承担 CEX 的提现手续费。

从根本上说,这种体验存在缺陷。用户访问 Web3 不应该依赖中心化交易所。用户不应该为管理 gas 而烦恼,而应该能够直接与去中心化系统交互。这并不是说不应该收取 gas 费用。Gas 的存在有其重要意义——它既能奖励网络运营者,又能防止垃圾信息和 DDoS 攻击。Gas 只需要更加灵活。如果某个 dApp 或游戏开发者拥有原生代币,该代币就应该可以作为支付 gas 的货币。

基于这一理念,CoinChoice 应运而生。

CoinChoice 运行在用户的浏览器中,为每笔交易提供以用户所选货币管理 gas 的能力。如果用户更倾向于持有 USDC 而非 ETH,则可以直接用 USDC 支付 gas。

CoinChoice 的 MetaMask Snap 进一步提升了使用体验。用户可以直接在 MetaMask 浏览器扩展中,用所选的 gas 货币充值 ETH。这使 MetaMask 成为替代访问中心化交易所的解决方案,整合并简化了 Web3 体验,同时帮助 Web3 新用户在不被日益增多的服务和平台所淹没的情况下,逐步熟悉部分系统。

请带我们了解一下技术实现?

CoinChoice 是一款 MetaMask 和浏览器扩展,让您可以自由选择用哪种代币支付 gas。

软件包

  • backend:一个 NestJS Web 服务器,负责——通过 Tenderly API 和 0x Quote API 模拟交易,以估算所选货币的 gas 费用

  • 在客户端的 MetaMask Snap 与浏览器扩展之间接收和转发消息

  • 接收元交易并转发至中继合约

  • browser-extension:基于 Plasmo 框架的浏览器扩展——拦截发往 MetaMask 的 RPC 请求,以注入兑换 ETH 用于 gas 融资的操作

  • 提供可选择 Gas 代币的弹窗

  • 提供由 Cypher Onboarding SDK 驱动的代币管理弹窗

  • 所有数据提交至后端服务,由其管理区块链交互和模拟

  • contracts:支持中继元交易的智能合约,用于在 0x Protocol 上促成兑换

  • frpc:利用 Fluence Network 的 fRPC 网关的软件包

  • meta-mask:包含 React.js 网站及相关 MetaMask Snap 的软件包——该 Snap 负责通过显示"需要 ETH?"消息,直接在 MetaMask 界面内提供 ETH 兑换功能

  • 该 Snap 通过向后端发送请求,将消息转发至客户端的浏览器扩展,从而实现 MetaMask Snap 触发浏览器扩展的体验

  • 相关网站用于支持 MetaMask Snap 的安装

技术架构

CoinChoice 是一款浏览器扩展,为交易提供以所选货币充值 gas 的功能。

该浏览器扩展设计为在每笔交易前提示用户以其所选货币支付 gas。

如果用户接受,系统将捕获一笔元交易,将估算数量的所选代币转换为 ETH。

该元交易被转发至后端服务,再由后端转发至中继智能合约并执行。

元交易结算完成后,由于已存入适量 gas,用户的原始交易将被提示并可正常执行。

MetaMask Snap 的设计目的是将原本孤立在 MetaMask 扩展内的交易转发至 CoinChoice 扩展。其实现方式如下:

  1. 将包含交易详情的请求提交至后端服务

  2. 后端通过 WebSocket 连接将交易数据转发至浏览器扩展

  3. 浏览器扩展收到此 WebSocket 消息后,启动以所选货币兑换自动估算数量 gas 的流程

作为消息总线的后端

由于 MetaMask Snap 的沙箱特性,CoinChoice 采用了将后端作为两个前端组件之间消息总线的方案。

Snap 的代码隔离机制阻止了与 MetaMask 扩展上下文之外的前端组件进行通信。

然而,Snap 可以发起服务端请求,因此可以使用一个能够管理用户会话的后端服务,在不同前端上下文之间传递消息。

这一机制可通过我们的源代码加以说明。

在 MetaMask Snap 中,我们向服务器提交请求。

参见 https://github.com/Coinchoice/coinchoice/blob/main/packages/meta-mask/packages/snap/src/index.ts#L12

import { OnTransactionHandler } from '@metamask/snaps-types';
import { heading, panel, text } from '@metamask/snaps-ui';

export interface JsonRpcRequest {
    id: string | undefined;
    jsonrpc: '2.0';
    method: string;
    params?: Array<any>;
}

const onTriggerAPI = async (transaction: JsonRpcRequest) => {
    await fetch(`https://api.coinchoice.link/metamask`, {
            method: 'POST',
            body: JSON.stringify(transaction),
            headers: { 'Content-Type': 'application/json' },
        })
    .then((res) => {
        if (!res.ok) {
            throw new Error('Bad response from server');
        }
        return res.json();
    })
    .then((json) => console.log(json))
    .catch((err) => console.error(err));
};

// Handle outgoing transactions
export const onTransaction: OnTransactionHandler = async ({ transaction }) => {
    // trigger browser extension for swap
    const response = await onTriggerAPI(transaction);
    // this is shown if no transaction is triggered
    if (typeof transaction.data === 'string' && transaction.data !== '0x') {
        return {
            content: panel([
                heading('Need ETH for gas?'),
                text('Receive ETH gasless for any currency with Coinchoice!'),
            ]),
        };
    }

    const currentGasPrice = await ethereum.request<string>({
        method: 'eth_gasPrice',
    });

    const transactionGas = parseInt(transaction.gas as string, 16);
    const currentGasPriceInWei = parseInt(currentGasPrice ?? '', 16);
    const maxFeePerGasInWei = parseInt(transaction.maxFeePerGas as string, 16);
    const maxPriorityFeePerGasInWei = parseInt(transaction.maxPriorityFeePerGas as string, 16);

    const gasFees = Math.min(
        maxFeePerGasInWei * transactionGas,
        (currentGasPriceInWei + maxPriorityFeePerGasInWei) * transactionGas
    );

    const transactionValueInWei = parseInt(transaction.value as string, 16);
    const gasFeesPercentage = (gasFees / (gasFees + transactionValueInWei)) * 100;

    return {
        content: panel([
            heading('Need ETH for gas?'),
            text(
                `As setup, you are paying **${gasFeesPercentage.toFixed(
                2
                )}%** in gas fees for this transaction.`
            ),
            text(`Also: **${response}**`),
        ]),
    };
};
复制

然后,在服务器端,我们使用接收到的交易载荷中的 from 参数来识别钱包及其关联的会话标识符。

参见: https://github.com/Coinchoice/coinchoice/blob/main/packages/backend/src/app.controller.ts#LL36-L49C3

@Post('metamask')
async metamask(@Body() data: any) {
    const address = data.from;
    const wallet = await this.walletService.findOne(address);
    if (wallet) {
        console.log(`clientId for ${address} is: ${wallet.clientId}`);
        if (wallet.clientId) {
            this.appGateway.server.to(wallet.clientId).emit('onMetamask', {
                msg: data,
                content: 'Hello from the backend!',
            });
        } else throw new NotFoundException('ClientId not found for this wallet');
    } else throw new NotFoundException('Wallet not found');
}
复制

最后,在浏览器扩展中,我们通过 WebSocket 连接连接至后端服务,连接本身即建立了用户会话标识符。参见: https://github.com/Coinchoice/coinchoice/blob/main/packages/browser-extension/src/contents/main.ts#L107

const socket = io(API_HOST);
socket.on('connect', function () {
    console.log('CS SOCKET: Connected');
});
socket.on('events', function (data) {
    console.log('CS SOCKET: event', data);
});
socket.on('onMessage', async function (data) {
    console.log('CS SOCKET: onMessage', data);
    if (data.msg.startsWith('Hello client')) {
        const [_, clientId] = data.msg.split('#');
        wallet.clientId = clientId;
        facade.setWallet(wallet);
        try {
            await busPromise('connect-wallet', { wallet });
        } catch (e) {
            console.log('CS SOCKET ERROR: onMessage - connect wallet');
            console.error(e);
        }
    }
});
socket.on('onMetamask', async function (data) {
    console.log('CS SOCKET: onMetamask', data);
    // On Metamask, open the Notification.
    const { msg: tx }: { msg: TxRequest } = data;
    // Cancel the original tx

    await facade.waitForSignature({ method: 'need_eth', params: [tx] });
});
复制

在此代码片段中,我们可以看到通过 onMetamask 消息接收到的交易数据如何触发签名提示(元交易)。

用于安装 Snap 的网站

要安装 MetaMask Snap,网站必须调用相应的 JSON RPC 端点。

/**
 * Connect a snap to MetaMask.
 *
 * @param snapId - The ID of the snap.
 * @param params - The params to pass with the snap to connect.
 */
export const connectSnap = async (
  snapId: string = defaultSnapOrigin,
  params: Record<'version' | string, unknown> = {},
) => {
  await window.ethereum.request({
    method: 'wallet_requestSnaps',
    params: {
      [snapId]: params,
    },
  });
};
复制

托管于 https://cchoice.xyz/ 的 CoinChoice MetaMask Snap 安装网站,参考了 MetaMask 团队的代码库:https://github.com/ziad-saab/snappy-recovery

开始使用 CoinChoice

如需开始使用,您可以通过以下指南安装支持测试网的 CoinChoice 版本:https://app.coinchoice.link/

如果您要继续实现这个项目,下一步计划是什么?

CoinChoice 已在 Goerli 以太坊测试网上完成实现和测试,尽管目前仍处于较为实验性的阶段。

假设各方激励机制一致,能够支持 CoinChoice 的持续开发,下一步将是为所有主要 EVM 兼容区块链启用 CoinChoice,并尽可能将浏览器扩展推广至更多用户。

曝光度和分发渠道始终是挑战,因此需要投入资源进行社区建设,并与 Web3 平台——尤其是跨链平台——建立合作关系,共同解决 gas 需求方面的顾虑。

一旦应用投入使用并收集到终端用户的反馈,便可确定并实施错误修复和其他改进,进一步提升终端用户体验。

在未来发展方面,早期曾考虑但因调用者上下文相关问题而未实现的账户抽象(Account Abstraction)方案可能会被重新纳入考量。CoinChoice 的核心功能也可以模块化,使 dApp 开发者能够直接将 gas 管理嵌入其应用程序中。

能介绍一下您自己和您的团队吗?

CoinChoice 团队背景多元,成员来自截然不同的生活经历,但共同怀揣同一目标——为去中心化的未来打造出色的软件。

我是 Ryan,负责创建 CoinChoice 浏览器扩展并支持整体解决方案架构的工程师。我通过 EthDenver 的 Discord 寻找团队成员,所有成员也正是在丹佛第一次线下相聚。我们每个人都有独特的技能,使我们能够高效协作。面对分歧和问题,我们迎难而上,荣辱与共。经历这次黑客松,团队可以自豪地说,共事是一段愉快的经历。如果这次合作能带来新的机遇,我们都愿意继续相互支持。

就团队经验而言,Achim 是经济学与数学交叉领域工程师的最佳代表。Achim 开发了一个名为 1Delta.io 的 DeFi 协议,正在突破杠杆数字资产管理的边界。这一经验对于以生产级安全标准开发和部署 CoinChoice 智能合约至关重要。

Andre 拥有 Web2 背景,因此是开发和管理服务端流程的最佳人选,这些流程对于管理和执行元交易及其他应用数据不可或缺。Andrew 领导着巴西的 Web2 机构 Web Eleven。

Christie 从非技术层面为团队提供支持,负责演示文稿制作、品牌建设、内容创作和项目管理。Christie 领导着澳大利亚的 Web2 机构 Web Doodle。

至于我自己,我拥有解决方案架构和工程背景。我主导着一家名为 Usher.so 的 Web3 初创公司,我们开发了一个合作伙伴计划平台,并正在探索新的去中心化基础设施来支撑该平台。在负责前端开发和解决方案架构的同时,我将协调项目和团队的经验带到了 EthDenver 黑客松中。

您认为 MetaMask Snaps 为 Web3 领域带来了哪些机遇?

MetaMask 正将自己定位为用户进入 Web3 时最先接触的应用之一。通过开放用户体验的可扩展性,MetaMask 让开发者能够在一个早期且熟悉的触点上展示其价值主张。随着面向下一代 Web3 用户的 Web3 入口趋于整合,这一点将变得愈发重要。这与我们已经熟悉的互联网入口整合趋势如出一辙——社交网络或搜索引擎等面向终端用户的应用,已成为众多互联网用户的第一交互入口。

MetaMask Snaps 能够实现更具情境感知的体验。一个可能的应用场景是:某个 Snap 根据 MetaMask 钱包正在交互的 dApp 而呈现不同的功能。通过让开发者在 Web3 的叠加用户界面上进行构建,可以融入更多有趣的创新,从根本上改变用户与 Web3 的交互方式。与只为特定协议提供 UI 的 dApp 不同,Snap 开发者可以改变所有协议的交互方式,因为创新就存在于支撑这些交互的钱包之中。

最后,旨在提升交易透明度、简化 Web3 交互并为终端用户提供额外安全保障的 MetaMask Snaps,将从根本上使 Web3 对大量用户而言更加安全、更具可行性。

您对有意尝试 MetaMask Snaps 的开发者有什么建议?

参阅实现 MetaMask Snaps 的原始源代码,深入了解这些新功能的运作方式。MetaMask Snaps 团队在公开展示 MetaMask Snaps 项目的完善代码方面做得非常出色。文档提供了功能概览,而源代码则展示了这些功能的具体实现和使用方式。

在早期开发阶段,请对 Snaps 框架的能力保持合理预期。尽管 MetaMask Snaps 最终将实现 MetaMask 的完整可扩展性,但 Snaps 框架中各组件的访问权限将逐步开放。MetaMask 在最大化终端用户安全性方面需要权衡取舍,因此早期版本中的某些功能可能会被重新评估。如果您的解决方案需要更多地访问用户的浏览器上下文,可以参考 CoinChoice 源代码,开发一个由 MetaMask Snap 内的操作触发的浏览器扩展。

联系 MetaMask 团队。MetaMask 团队高度投入于实现可扩展性,并积极寻求反馈和机会,以更好地兑现这一价值主张。

使用 MetaMask Snaps 进行构建

开始使用 MetaMask Snaps:

  1. 查阅开发者文档

  2. 安装 MetaMask Flask

  3. 查看 MetaMask Snaps 指南

  4. 在 Twitter、GitHub 讨论区和 Discord 上与我们保持联系

关注我们的团队,期待在您附近的下一场黑客松与您相遇!祝 BUIDL 愉快 ⚒️

免责声明:MetaMask Snaps 通常由 Consensys Software 以外的第三方开发。使用第三方开发的 MetaMask Snaps 须由您自行决定并承担风险,且您同意对因此类活动导致的任何损失或损害承担全部责任。Consensys 对第三方开发的 MetaMask Snaps 不作任何明示或暗示的保证,并对第三方开发的 Snaps 免除一切责任。使用区块链相关软件存在风险,使用 MetaMask Snaps 即表示您完全接受并承担这些风险。

AI 翻译。可能包含错误。请务必核实信息。

评价此翻译

本文作者:

  • MetaMask

    MetaMask(前身为 Consensys Software Inc)是全球最大的自托管金融平台,为用户提供一站式服务,涵盖加密资产与传统资产的持有、消费、储蓄与增值。公司正在打造实现上述功能的消费级平台,将支付、储蓄、投资与数字资产整合于一体,带来无缝一体化的使用体验。MetaMask 脱胎于全球使用最广泛的自托管钱包,让用户直接掌控自己的资金与资产,覆盖约 190 个国家和地区。自 2016 年起,MetaMask 在以太坊的发展历程中扮演了举足轻重的奠基角色。如今,MetaMask 处于区块链经济的核心位置,致力于构建「开放货币」的操作系统,让每个人都能全面掌控自己的财务生活。

    阅读所有文章