CoinChoice Snap: Recarga Gas con Cualquier Moneda

Ganador del Hackathon ETHDenver

11 min de lectura
CoinChoice Snap: Recarga Gas con Cualquier Moneda

MetaMask Snaps es la hoja de ruta para convertir a MetaMask en la wallet más extensible del mundo. Como desarrollador, puedes llevar tus funciones y APIs a MetaMask de formas completamente nuevas. Los desarrolladores de Web3 son el núcleo de este crecimiento, y esta serie tiene como objetivo destacar los novedosos MetaMask Snaps que se están construyendo hoy en día.

CoinChoice Snap

Repositorio del Snap: https://github.com/coinchoice/coinchoice/

¿Por qué lo construiste?

CoinChoice surgió de un problema pequeño y tedioso, pero muy común: un usuario de Web3 puede tener una wallet con tokens de valor pero sin la moneda de gas necesaria para realizar transacciones.

Este problema se acentúa en usuarios con liquidez fraccionada. Un usuario que gestiona múltiples wallets y aprovecha oportunidades en distintos ecosistemas blockchain se verá obligado a distribuir sus tokens de gas entre cada una de sus wallets, reservando capital que de otro modo destinaría a sus inversiones, con el fin de facilitar sus activos digitales y transacciones. Más allá de la gestión del gas, los tokens transferidos a una red donde no se posee gas obligan al usuario a obtenerlo antes de poder realizar cualquier acción adicional. Esto generalmente implica acceder a un Exchange Centralizado para obtener y retirar gas, lo que expone al usuario a las comisiones de retiro de los CEX.

En esencia, la experiencia está mal planteada. No debería existir una dependencia de los exchanges centralizados para que los usuarios accedan a Web3. Los usuarios no deberían preocuparse por gestionar su gas; en cambio, deberían poder interactuar simplemente con sistemas descentralizados. Esto no significa que el gas no deba ser necesario. El gas cumple un propósito fundamental: recompensar a los operadores de una red y prevenir el spam y los ataques DDoS. El gas simplemente debería ser más flexible. Si una dApp o un desarrollador de juegos tiene un token nativo, ese token debería ser suficiente como moneda para pagar el gas.

Con esta filosofía en mente, se desarrolló CoinChoice.

CoinChoice reside en el navegador del usuario y complementa cada transacción con la posibilidad de gestionar el gas en la moneda que el usuario prefiera. Si el usuario prefiere mantener USDC en lugar de ETH, el gas puede pagarse en USDC.

El MetaMask Snap de CoinChoice complementa aún más la experiencia. Directamente desde la extensión de navegador de MetaMask, los usuarios pueden recargar su ETH a cambio de la moneda de gas que hayan elegido. Esto convierte a MetaMask en una solución que reemplaza la visita a un Exchange Centralizado, consolidando y simplificando la experiencia en Web3, y permitiendo que los nuevos usuarios de Web3 se familiaricen con un subconjunto de sistemas sin verse abrumados por la creciente abundancia de servicios y plataformas.

¿Puedes explicarnos la implementación técnica?

CoinChoice es una extensión de navegador y MetaMask que te da la posibilidad de elegir qué monedas se utilizan para pagar el gas.

Paquetes

  • backend: Un servidor web NestJS responsable de simular transacciones contra la API de Tenderly y la API de cotización de 0x para obtener estimaciones de gas en la moneda elegida

  • recibir y distribuir mensajes entre el MetaMask Snap y la extensión de navegador del cliente

  • recibir meta-transacciones para reenviarlas a los contratos de retransmisión

  • browser-extension: Una extensión de navegador impulsada por el framework Plasmo que intercepta solicitudes RPC a MetaMask para inyectar swaps a ETH para financiar el gas

  • ofrece una ventana emergente donde se puede seleccionar la moneda de gas

  • ofrece una ventana emergente para la gestión de tokens impulsada por el SDK de incorporación de Cypher

  • todos los datos se envían al servicio backend donde se gestionan las interacciones y simulaciones en blockchain

  • contracts: Smart Contracts que potencian la retransmisión de meta-transacciones para facilitar swaps en el protocolo 0x

  • frpc: Un paquete para aprovechar el gateway fRPC de la red Fluence

  • meta-mask: Un paquete que contiene un sitio en React.js y el MetaMask Snap asociado. El Snap se encarga de ofrecer swaps de ETH directamente dentro de la interfaz de MetaMask, mostrando el mensaje "¿Necesitas ETH?"

  • El Snap funciona realizando una solicitud al backend para que se reenvíe un mensaje a la extensión de navegador del cliente, habilitando una experiencia en la que el MetaMask Snap puede iniciar la extensión de navegador

  • El sitio asociado se utiliza para habilitar la instalación del MetaMask Snap

Configuración técnica

CoinChoice es una extensión de navegador que complementa las transacciones con la posibilidad de recargar gas con la moneda elegida.

La extensión de navegador está diseñada para solicitar al usuario que pague el gas en la moneda seleccionada antes de cada transacción.

Si el usuario acepta, se captura una meta-transacción para convertir una cantidad estimada del token elegido a ETH.

Esta meta-transacción se reenvía al servicio backend, que la envía al Smart Contract de retransmisión donde se ejecuta.

Una vez que la meta-transacción se liquida, la transacción original del usuario se solicita y es accesible gracias al depósito de la cantidad adecuada de gas.

El MetaMask Snap está diseñado para reenviar una transacción que de otro modo quedaría aislada dentro de la extensión de MetaMask hacia la extensión de CoinChoice. Lo hace de la siguiente manera:

  1. enviando una solicitud con los detalles de la transacción al servicio backend

  2. el backend reenvía los datos de la transacción a la extensión de navegador a través de una conexión websocket

  3. una vez que la extensión de navegador recibe este mensaje websocket, inicia su flujo de recarga de una cantidad de gas estimada automáticamente a cambio de la moneda seleccionada.

El backend como bus de mensajes

CoinChoice adoptó el enfoque de utilizar un backend como bus de mensajes entre los dos componentes frontend debido a la naturaleza aislada del MetaMask Snap.

El aislamiento del código del Snap impide la comunicación con componentes frontend que residen en contextos fuera de la extensión de MetaMask.

Sin embargo, los Snaps pueden facilitar solicitudes del lado del servidor, por lo que un servicio backend capaz de gestionar sesiones de usuario puede utilizarse para pasar mensajes entre los contextos frontend.

Un ejemplo de esto puede demostrarse a través de nuestro código fuente.

Dentro del MetaMask Snap, enviamos una solicitud al servidor.

Ver 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}**`),
        ]),
    };
};
Copiar

Luego, dentro del servidor, utilizamos el parámetro from dentro del payload de la transacción recibida para identificar la wallet y su identificador de sesión asociado.

Ver: 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');
}
Copiar

Finalmente, dentro de la extensión de navegador, nos conectamos al servicio backend a través de una conexión websocket, donde la propia conexión establece el identificador de sesión del usuario. Ver: 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] });
});
Copiar

En este fragmento de código, podemos ver cómo los datos de la transacción recibidos a través del mensaje onMetamask inician la solicitud de firma (meta-transacción).

Sitio web para la instalación del Snap

Para instalar un MetaMask Snap, un sitio web debe llamar al endpoint JSON RPC correspondiente.

/**
 * 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,
    },
  });
};
Copiar

El sitio web de CoinChoice para instalar el MetaMask Snap, alojado en https://cchoice.xyz/, se inspiró en el repositorio del equipo de MetaMask: https://github.com/ziad-saab/snappy-recovery

Primeros pasos con CoinChoice

Para comenzar, puedes instalar la versión habilitada para testnet de CoinChoice siguiendo la guía disponible en: https://app.coinchoice.link/

¿Cuáles son los próximos pasos si implementaras esto?

CoinChoice ha sido implementado y probado en la testnet Goerli de Ethereum, aunque de forma bastante experimental.

Suponiendo que los incentivos estén alineados para permitir el desarrollo continuo de CoinChoice, los próximos pasos serían habilitar CoinChoice para todas las blockchains principales compatibles con EVM y luego poner la extensión de navegador en manos del mayor número posible de usuarios.

La visibilidad y la distribución siempre son un desafío, por lo que requerirán inversión en el desarrollo de comunidad y alianzas con plataformas Web3, especialmente plataformas cross-chain, que busquen aliviar esta preocupación en torno a los requisitos de gas.

Una vez que la aplicación sea utilizada y se recopile feedback de los usuarios finales, se podrán identificar y aplicar correcciones de errores y otras mejoras para seguir perfeccionando la experiencia del usuario final.

En cuanto a desarrollos futuros, podría reconsiderarse una propuesta inicial de Account Abstraction que no fue implementada debido a problemas asociados con el contexto del llamador. La funcionalidad principal de CoinChoice también podría modularizarse para permitir a los desarrolladores de dApps integrar fácilmente la gestión de gas en sus aplicaciones.

¿Puedes contarnos un poco sobre ti y tu equipo?

El equipo de CoinChoice es bastante diverso, proveniente de entornos muy diferentes, pero con el mismo objetivo común: crear software increíble para un futuro descentralizado.

Soy Ryan, el ingeniero que creó la extensión de navegador de CoinChoice y apoyó la arquitectura de la solución. Busqué miembros del equipo a través del Discord de EthDenver. Fue en Denver donde todos los miembros del equipo se conocieron en persona. Cada uno de nosotros tiene un conjunto de habilidades único que nos permitió trabajar muy bien juntos. Los desacuerdos y los problemas se enfrentaron de frente, y tanto los errores como los éxitos fueron compartidos por igual entre todos los miembros del equipo. A través de la experiencia del hackathon, el equipo puede decir con seguridad que fue un placer trabajar juntos. Si surgen oportunidades de este proyecto, todos estaríamos disponibles para apoyarnos mutuamente.

En cuanto a la experiencia del equipo, Achim es el mejor ejemplo de un ingeniero que combina economía y matemáticas. Achim ha desarrollado un protocolo DeFi llamado 1Delta.io que está ampliando los límites de lo posible en la gestión de activos digitales con apalancamiento. Esta experiencia fue fundamental para desarrollar y desplegar los Smart Contracts de CoinChoice con seguridad de nivel productivo.

Andre proviene de un entorno Web2 y fue, por tanto, el candidato ideal para desarrollar y gestionar los procesos del lado del servidor que son fundamentales para gestionar y ejecutar meta-transacciones, así como otros datos de la aplicación. Andrew lidera Web Eleven, una agencia Web2 en Brasil.

Christie apoyó al equipo de forma no técnica a través de la creación de presentaciones, branding, contenido y gestión de proyectos. Christie lidera Web Doodle, una agencia Web2 en Australia.

En cuanto a mí, tengo experiencia en arquitectura de soluciones e ingeniería. Dirijo una startup Web3 llamada Usher.so donde hemos desarrollado una plataforma de programas de socios y estamos explorando nueva infraestructura descentralizada para dar soporte a dicha plataforma. Siendo responsable del desarrollo frontend y la arquitectura de soluciones, logré aportar mi experiencia coordinando un proyecto y un equipo al hackathon de EthDenver.

¿Qué oportunidades ves con MetaMask Snaps y las posibilidades que abre para el espacio Web3?

MetaMask se está posicionando como una de las primeras aplicaciones con las que un usuario interactúa para adentrarse en Web3. Al habilitar la extensibilidad de la experiencia del usuario, MetaMask permite a los desarrolladores introducir su propuesta de valor en un punto de contacto temprano y familiar. Esto cobra mayor importancia una vez que se produzca una consolidación en las puertas de entrada a Web3, especialmente para la próxima generación de usuarios de Web3. Esto es análogo a la consolidación de las puertas de entrada a internet que todos ya conocemos, donde aplicaciones orientadas al usuario final como las redes sociales o los motores de búsqueda se han convertido en el primer punto de interacción para muchos usuarios de internet.

MetaMask Snaps permite experiencias más contextuales. Un ejemplo podría ser un Snap que funcione de manera diferente según la dApp con la que esté interactuando la wallet de MetaMask. Al permitir a los desarrolladores construir sobre la interfaz de usuario superpuesta a Web3, se pueden incorporar desarrollos más interesantes que cambien fundamentalmente la forma en que se interactúa con Web3. A diferencia de una dApp que ofrece una interfaz para un protocolo específico, los desarrolladores de Snaps pueden modificar la forma en que se interactúa con todos los protocolos, ya que la innovación reside dentro de la wallet que habilita estas interacciones.

Por último, los MetaMask Snaps que buscan aumentar la transparencia de las transacciones, simplificar las interacciones en Web3 y proporcionar mayor seguridad a los usuarios finales harán que Web3 sea fundamentalmente más seguro y viable para un gran número de usuarios.

¿Algún consejo que quieras compartir con los desarrolladores interesados en probar MetaMask Snaps?

Consulta el código fuente original que implementa MetaMask Snaps para obtener una comprensión más profunda de cómo funcionan estas nuevas capacidades. El equipo de MetaMask Snaps ha hecho un excelente trabajo al publicar código bien desarrollado que demuestra el uso de MetaMask Snaps dentro de un proyecto. Si bien la documentación ofrece una visión general de las funciones, el código fuente ilustra cómo se implementan y manipulan estas funciones.

Reduce las expectativas sobre lo que es posible dentro del framework de Snaps durante las primeras etapas de desarrollo. Si bien MetaMask Snaps eventualmente habilitará la extensibilidad completa de MetaMask, el acceso a los diferentes componentes dentro del framework de Snaps se irá liberando de forma gradual. MetaMask deberá encontrar un equilibrio al maximizar la seguridad del usuario final, por lo que algunas funciones incluidas en las primeras versiones podrían ser reconsideradas. Si tu solución requiere mayor acceso al contexto del navegador del usuario, considera consultar el código fuente de CoinChoice para desarrollar una extensión de navegador que sea activada por una acción dentro de un MetaMask Snap.

Contacta al equipo de MetaMask. El equipo de MetaMask está muy comprometido con la extensibilidad y busca activamente feedback y oportunidades para mejorar esta propuesta de valor.

Construyendo con MetaMask Snaps

Para comenzar con MetaMask Snaps:

  1. Consulta la documentación para desarrolladores

  2. Instala MetaMask Flask

  3. Revisa una guía de MetaMask Snaps

  4. Mantente conectado con nosotros en Twitter, discusiones de GitHub y Discord

¡Estén atentos a nuestro equipo en el próximo hackathon cerca de ti! ¡Feliz BUIDLing! ⚒️

Aviso legal: Los MetaMask Snaps son generalmente desarrollados por terceros distintos a Consensys Software. El uso de MetaMask Snaps desarrollados por terceros se realiza a tu propia discreción y riesgo, y con el acuerdo de que serás el único responsable de cualquier pérdida o daño que resulte de dichas actividades. Consensys no ofrece ninguna garantía expresa o implícita, ya sea oral o escrita, con respecto a los MetaMask Snaps desarrollados por terceros, y renuncia a toda responsabilidad por los Snaps desarrollados por terceros. El uso de software relacionado con blockchain conlleva riesgos, y los asumes en su totalidad al utilizar MetaMask Snaps.

Traducido por IA. Puede contener errores. Por favor, verifique siempre la información.

Califica la traducción
  • MetaMask
    MetaMask

    La billetera cripto autocustodiada líder y puerta de entrada a Web3, desarrollada por Consensys.

    Leer todos los artículos