CoinChoice Snap: пополняйте газ любой валютой

Победитель хакатона ETHDenver

11 мин чтения
CoinChoice Snap: пополняйте газ любой валютой

MetaMask Snaps — это дорожная карта по превращению MetaMask в самый расширяемый кошелёк в мире. Как разработчик, вы можете добавлять свои функции и API в MetaMask совершенно новыми способами. Web3-разработчики — это основа этого роста, и данная серия материалов призвана продемонстрировать новаторские MetaMask Snaps, создаваемые прямо сейчас.

CoinChoice Snap

Snap Repo: https://github.com/coinchoice/coinchoice/

Почему вы его создали?

CoinChoice был разработан в ответ на небольшую, но крайне распространённую проблему: у Web3-пользователя может быть кошелёк с токенами, имеющими ценность, но без газовой валюты для совершения транзакций.

Эта проблема особенно остро стоит для пользователей с раздробленной ликвидностью. Пользователь, управляющий несколькими кошельками и использующий возможности в различных блокчейн-экосистемах, вынужден распределять газовые токены по каждому из своих кошельков, резервируя капитал, который иначе мог бы быть направлен на инвестиции, — только для того, чтобы обеспечить работу своих цифровых активов и транзакций. Помимо управления газом, токены, переведённые в сеть, где газ не принадлежит пользователю, требуют его получения перед любыми дальнейшими действиями. Как правило, это означает обращение к централизованной бирже для получения и вывода газа — что влечёт за собой комиссии за вывод средств с CEX.

По своей сути этот опыт несовершенен. Не должно быть зависимости от централизованных бирж для доступа пользователей к Web3. Пользователи не должны беспокоиться об управлении газом — вместо этого они должны просто взаимодействовать с децентрализованными системами. Это не означает, что газ не должен требоваться. Газ выполняет важнейшую функцию: вознаграждает операторов сети и предотвращает спам/DDoS-атаки. Газ должен просто быть более гибким. Если у dApp или разработчика игры есть нативный токен, этот токен должен быть достаточным для оплаты газа.

Именно с этой философией и был разработан CoinChoice.

CoinChoice работает в браузере пользователя, дополняя каждую транзакцию возможностью управлять газом в той валюте, которую предпочитает пользователь. Если пользователь предпочитает держать USDC вместо ETH, газ можно оплачивать в USDC.

MetaMask Snap от CoinChoice ещё больше улучшает этот опыт. Прямо в расширении MetaMask для браузера пользователи могут пополнять свой ETH в обмен на выбранную газовую валюту. Это превращает MetaMask в решение, заменяющее посещение централизованной биржи, консолидируя и упрощая Web3-опыт, а также позволяя новым Web3-пользователям освоить часть систем, не теряясь в постоянно растущем многообразии сервисов и платформ.

Расскажите о технической реализации?

CoinChoice — это расширение MetaMask и браузера, которое предоставляет вам выбор того, какие монеты использовать для оплаты газа.

Пакеты

  • backend: Веб-сервер на NestJS, отвечающий за симуляцию транзакций через Tenderly API и 0x Quote API для получения оценок газа в выбранной валюте

  • приём и передачу сообщений между MetaMask Snap и расширением браузера клиента

  • приём meta-tx для пересылки в контракты-ретрансляторы

  • browser-extension: Расширение браузера на базе Plasmo Framework, которое перехватывает RPC-запросы к MetaMask для внедрения свопов в ETH с целью финансирования газа

  • предлагает всплывающее окно для выбора Gas Coin

  • предлагает всплывающее окно для управления токенами на базе Cypher Onboarding SDK

  • все данные отправляются в бэкенд-сервис, где управляются взаимодействия с блокчейном и симуляции

  • contracts: Смарт-контракты, обеспечивающие ретрансляцию мета-транзакций для проведения свопов по протоколу 0x

  • frpc: Пакет для использования fRPC Gateway сети Fluence

  • meta-mask: Пакет, содержащий сайт на React.js и связанный MetaMask Snap. Snap отвечает за предложение ETH-свопов непосредственно в интерфейсе MetaMask, отображая сообщение «Need ETH?»

  • Snap работает, отправляя запрос на бэкенд, чтобы сообщение было переслано в расширение браузера клиента, обеспечивая взаимодействие, при котором MetaMask Snap может инициировать расширение браузера

  • Связанный сайт используется для установки MetaMask Snap

Техническая настройка

CoinChoice — это расширение браузера, которое дополняет транзакции возможностью пополнить газ в выбранной валюте.

Расширение браузера предназначено для того, чтобы предлагать пользователю оплатить газ в выбранной им валюте перед каждой транзакцией.

Если пользователь соглашается, захватывается мета-транзакция для конвертации расчётного количества выбранного токена в ETH.

Эта мета-транзакция пересылается в бэкенд-сервис, который направляет её в смарт-контракт-ретранслятор, где она и исполняется.

После завершения мета-транзакции исходная транзакция пользователя запрашивается и становится доступной благодаря депозиту соответствующего количества газа.

MetaMask Snap предназначен для пересылки транзакции, которая иначе была бы изолирована внутри расширения MetaMask, в расширение CoinChoice. Это происходит следующим образом:

  1. отправка запроса с деталями транзакции в бэкенд-сервис

  2. бэкенд пересылает данные транзакции в расширение браузера через websocket-соединение

  3. как только расширение браузера получает это websocket-сообщение, оно инициирует процесс пополнения автоматически рассчитанного количества газа в обмен на выбранную валюту

Бэкенд как шина сообщений

CoinChoice использовал подход с применением бэкенда в качестве шины сообщений между двумя фронтенд-компонентами из-за изолированной природы MetaMask Snap.

Изоляция кода Snap предотвращает взаимодействие с фронтенд-компонентами, находящимися в контекстах за пределами расширения MetaMask.

Однако Snaps могут выполнять серверные запросы, и поэтому бэкенд-сервис, способный управлять пользовательскими сессиями, может использоваться для передачи сообщений между фронтенд-контекстами.

Пример этого можно продемонстрировать через наш исходный код.

Внутри 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 endpoint.

/**
 * 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,
    },
  });
};
Копировать

Сайт CoinChoice для установки MetaMask Snap, размещённый по адресу https://cchoice.xyz/, был вдохновлён репозиторием команды MetaMask: https://github.com/ziad-saab/snappy-recovery

Начало работы с CoinChoice

Чтобы начать, вы можете установить версию CoinChoice для тестовой сети, воспользовавшись руководством по адресу: https://app.coinchoice.link/

Каковы следующие шаги в случае дальнейшей реализации?

CoinChoice был реализован и протестирован для тестовой сети Goerli Ethereum, хотя и носит экспериментальный характер.

При условии согласования стимулов для продолжения разработки CoinChoice следующими шагами стали бы: поддержка всех основных EVM-совместимых блокчейнов, а затем распространение расширения браузера среди как можно большего числа пользователей.

Охват и дистрибуция всегда представляют собой вызов, и поэтому потребуют инвестиций в развитие сообщества и партнёрства с Web3-платформами, особенно кросс-чейн платформами, стремящимися снять озабоченность по поводу требований к газу.

После того как приложение начнёт использоваться и будут собраны отзывы конечных пользователей, можно будет определить и применить исправления ошибок и другие улучшения для дальнейшего совершенствования пользовательского опыта.

Что касается будущих разработок, может быть пересмотрена ранняя концепция Account Abstraction, которая не была реализована из-за проблем, связанных с контекстом вызывающей стороны. Основная функциональность CoinChoice также может быть модуляризирована, чтобы позволить разработчикам dApp просто встраивать управление газом в свои приложения.

Расскажите немного о себе и своей команде?

Команда CoinChoice весьма разнообразна — её участники пришли из совершенно разных сфер жизни, но разделяют одну общую цель: создавать отличное программное обеспечение для децентрализованного будущего.

Я — Райан, инженер, создавший расширение браузера CoinChoice и поддержавший архитектуру решения. Я искал членов команды через Discord EthDenver. Именно в Денвере все участники команды впервые встретились лично. У каждого из нас есть уникальный набор навыков, который позволил нам очень хорошо работать вместе. Разногласия и проблемы решались напрямую, а ответственность и успех делились поровну между всеми членами команды. По итогам хакатона команда может с уверенностью сказать, что работать вместе было одним удовольствием. Если из этого начинания возникнут новые возможности, мы все готовы поддержать друг друга.

Что касается опыта команды, Ахим — лучший пример инженера, объединяющего экономику и математику. Ахим разработал DeFi-протокол 1Delta.io, который раздвигает границы возможного в области управления цифровыми активами с кредитным плечом. Этот опыт был жизненно важен для разработки и развёртывания смарт-контрактов CoinChoice с производственным уровнем безопасности.

Андре пришёл из Web2-среды и поэтому стал идеальным кандидатом для разработки и управления серверными процессами, необходимыми для управления и исполнения мета-транзакций, а также других данных приложения. Эндрю руководит Web Eleven, Web2-агентством в Бразилии.

Кристи поддерживала команду нетехнически — через создание презентаций, брендинг, контент и управление проектом. Кристи руководит Web Doodle, Web2-агентством в Австралии.

Что касается меня, у меня есть опыт в архитектуре решений и инженерии. Я руковожу Web3-стартапом Usher.so, где мы разработали платформу партнёрских программ и в настоящее время исследуем новую децентрализованную инфраструктуру для её поддержки. Будучи ответственным за фронтенд-разработку и архитектуру решений, я привнёс свой опыт координации проекта и команды на хакатон EthDenver.

Какие возможности вы видите в MetaMask Snaps и то, что это открывает для пространства Web3?

MetaMask позиционирует себя как одно из первых приложений, с которым пользователь взаимодействует при входе в Web3. Обеспечивая расширяемость пользовательского опыта, MetaMask позволяет разработчикам представлять своё ценностное предложение на раннем и знакомом этапе взаимодействия. Это становится всё более важным по мере консолидации шлюзов в Web3, особенно для следующего поколения Web3-пользователей. Это аналогично консолидации шлюзов в интернет, с которой мы все уже знакомы: пользовательские приложения, такие как социальные сети или поисковые системы, стали первой точкой взаимодействия для многих интернет-пользователей.

MetaMask Snaps открывает возможности для более контекстуальных взаимодействий. Например, можно создать Snap, который функционирует по-разному в зависимости от того, с каким dApp взаимодействует кошелёк MetaMask. Предоставляя разработчикам возможность строить поверх оверлейного пользовательского интерфейса Web3, можно внедрять более интересные разработки, которые фундаментально изменят способ взаимодействия с Web3. В отличие от dApp, предоставляющего UI для конкретного протокола, разработчики Snap могут изменить способ взаимодействия со всеми протоколами, поскольку инновация живёт внутри кошелька, обеспечивающего эти взаимодействия.

Наконец, MetaMask Snaps, направленные на повышение прозрачности транзакций, упрощение Web3-взаимодействий и обеспечение дополнительной безопасности конечных пользователей, фундаментально сделают Web3 более безопасным и доступным для огромного числа пользователей.

Есть ли советы, которыми вы хотели бы поделиться с разработчиками, желающими попробовать MetaMask Snaps?

Обращайтесь к исходному коду, реализующему MetaMask Snaps, чтобы получить более глубокое понимание того, как работают эти новые возможности. Команда MetaMask Snaps проделала отличную работу, открыв хорошо разработанный код, демонстрирующий MetaMask Snaps в рамках проекта. Хотя документация даёт обзор функций, исходный код показывает, как эти функции реализуются и используются.

Снизьте ожидания относительно возможностей Snaps Framework на ранних этапах разработки. Хотя MetaMask Snaps в конечном итоге обеспечит полную расширяемость MetaMask, доступ к различным компонентам Snaps Framework будет открываться постепенно. MetaMask придётся соблюдать баланс при максимизации безопасности конечных пользователей, поэтому некоторые функции, включённые в ранние сборки, могут быть пересмотрены. Если ваше решение требует большего доступа к контексту браузера пользователя, рассмотрите возможность обращения к исходному коду CoinChoice для разработки расширения браузера, запускаемого действием внутри MetaMask Snap.

Свяжитесь с командой MetaMask. Команда MetaMask активно работает над обеспечением расширяемости и ищет обратную связь и возможности для лучшей реализации этого ценностного предложения.

Разработка с MetaMask Snaps

Чтобы начать работу с MetaMask Snaps:

  1. Ознакомьтесь с документацией для разработчиков

  2. Установите MetaMask Flask

  3. Изучите руководство по MetaMask Snaps

  4. Оставайтесь на связи с нами в Twitter, GitHub discussions и Discord

Следите за нашей командой на следующем хакатоне рядом с вами! Удачного BUIDLing'а ⚒️

Отказ от ответственности: MetaMask Snaps, как правило, разрабатываются третьими сторонами, а не Consensys Software. Использование MetaMask Snaps, разработанных третьими сторонами, осуществляется по вашему собственному усмотрению и на ваш риск, и вы соглашаетесь с тем, что несёте единоличную ответственность за любые убытки или ущерб, возникшие в результате такой деятельности. Consensys не даёт никаких явных или подразумеваемых гарантий, устных или письменных, в отношении MetaMask Snaps, разработанных третьими сторонами, и отказывается от какой-либо ответственности за Snaps, разработанные третьими сторонами. Использование программного обеспечения, связанного с блокчейном, сопряжено с рисками, которые вы полностью принимаете на себя при использовании MetaMask Snaps.

Переведено ИИ. Может содержать ошибки. Пожалуйста, всегда проверяйте информацию.

Оцените перевод
  • MetaMask
    MetaMask

    Ведущий криптокошелек с самостоятельным хранением и шлюз к Web3, созданный Consensys.

    Читать все статьи