Saltar al contenido

Ether Eyes Snap: Predicción de precios de gas futuros

Ganador del Hackathon Inter IIT

13 min de lectura
Ether Eyes Snap: Predicción de precios de gas futuros

MetaMask Snaps es la hoja de ruta para convertir a MetaMask en la wallet más extensible del mundo. Como desarrollador, puedes incorporar 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.

Ether Eyes Snap

Repositorio del Snap: https://github.com/aritroCoder/EtherEyes

¿Por qué lo construiste?

Los precios del gas en la blockchain de Ethereum pueden ser muy difíciles de predecir, y esto representa un obstáculo a la hora de realizar pagos. Un usuario que intenta hacer un pago puede terminar pagando mucho más de lo necesario. Los picos de tráfico de corta duración en la blockchain pueden generar un gran impacto en los precios del gas. Aunque las L2 fueron creadas para reducir los precios del gas, a veces necesitamos una solución más general y sencilla que recurrir a una L2 para realizar transacciones. Pensamos que sería útil para los usuarios contar con una aplicación capaz de predecir los precios futuros del gas basándose en datos históricos y tendencias actuales.

¿Puedes explicarnos la implementación técnica?

EtherEyes consta de cuatro partes: el servidor de precios del gas, el snap, el modelo SARIMA y el frontend web. El servidor de precios del gas proporciona datos sobre los precios del gas históricos y actuales. El sitio web aloja el snap y ofrece una interfaz para instalarlo e interactuar con él mediante llamadas a los métodos de la API del snap. Utilizamos el modelo de Media Móvil Integrada Autorregresiva Estacional (SARIMA, por sus siglas en inglés) para predecir los precios del gas.

Servidor de Precios del Gas

Recopilamos datos de precios del gas en tiempo real en forma de una lista de aproximadamente 200 velas japonesas (cada vela es un agregado de los últimos 30 minutos de datos) utilizando la API de Owlracle. Esto nos proporciona los datos de series temporales de los precios del gas de las últimas 100 horas.

El Snap

Para el snap, utilizamos la API de transaction insights para ofrecer predicciones de precios del gas en tiempo real. El flujo del snap es el siguiente:

Primero, cuando el usuario inicia una transacción, los transaction insights aparecen en la página de confirmación, lo cual se implementa mediante el onTransactionHandler. Aquí mostramos al usuario la predicción para los próximos 30 y 60 minutos. A continuación se presenta una versión simplificada de lo que implementamos:

export const onTransaction: OnTransactionHandler = async ({ transaction }) => {
 let state: { notifToggle: boolean; urgency: number; lowEth: number };

 state = (await wallet.request({
   method: 'snap_manageState',
   params: ['get'],
 })) as { notifToggle: boolean; urgency: number; lowEth: number };

 if (!state) {
   state = {
     notifToggle,
     urgency,
     lowEth,
   };
 }

 let insights: any = {
   type: 'Gas Fee estimation',
 };

 if (
   !isObject(transaction) ||
   !hasProperty(transaction, 'data') ||
   typeof transaction.data !== 'string'
 ) {
   console.warn('unknown transaction type');
   return { insights };
 }

 const response = await fetch(`${MODEL_API_ENDPOINT}`, {
   method: 'get',
   headers: {
     'Content-Type': 'application/json',
   },
 });

 const currentData = await fetch(CURRENT_DATA_ENDPOINT, {
   method: 'get',
   headers: {
     'Content-Type': 'application/json',
   },
 });

 if (!response.ok) {
   throw new Error('API request failed');
 }

 const data = await response.json();
 const current = await currentData.json();

 state.lowEth = Math.min(data.low_30_minutes, data.low_60_minutes);

 await wallet.request({
   method: 'snap_manageState',
   params: ['update', state],
 });

 insights = {
   'Average Gas Limit': `Current value: ${current.avgGas}`,

   'Estimated current gas price': `Current value (Base + Priority): ${
     current.speeds[0].baseFee + current.speeds[0].maxPriorityFeePerGas
   } GWei; Base Fee: ${current.speeds[0].baseFee} GWei`,

   'Forecasted Avg Gas price (for the next 30 mins)': `
   Gas price within 30 minutes is expected to get as low as: ${data.low_30_minutes} GWei`,

   'Forecasted Avg Gas price (for the next 60 mins)': `Gas price within 60 minutes is expected to get as low as: ${Math.min(
     data.low_30_minutes,
     data.low_60_minutes,
   )} GWei`,

   'Expected savings in 30 mins (For average gas limit)': `
   ${
     current.avgGas *
     (current.speeds[0].baseFee +
       current.speeds[0].maxPriorityFeePerGas -
       data.low_30_minutes)
   } GWei`,

   'Expected savings in 60 mins (For average gas limit)': `${
     current.avgGas *
     (current.speeds[0].baseFee +
       current.speeds[0].maxPriorityFeePerGas -
       Math.min(data.low_30_minutes, data.low_60_minutes))
   } GWei
   `,
 };

 return { insights };
};
Copiar

Si el usuario decide esperar, puede hacerlo e iniciar un sistema de notificaciones desde el sitio web que interactúa con el snap a través del handler onCronjob y envía los precios del gas cada tres minutos como notificaciones nativas del navegador. Cuando los precios se acercan mucho a la predicción, le pide al usuario que complete la transacción.


export const onCronjob: OnCronjobHandler = async ({ request }) => {
 switch (request.method) {
   case 'exampleMethodOne': {
     // get data from state whenever snap is invoked as snaps executions are ephemeral
     let state: { notifToggle: boolean; urgency: number; lowEth: number };

     state = (await wallet.request({
       method: 'snap_manageState',
       params: ['get'],
     })) as { notifToggle: boolean; urgency: number; lowEth: number };

     if (!state) {
       state = {
         notifToggle,
         urgency,
         lowEth,
       };
     }
     notifToggle = state.notifToggle;
     urgency = state.urgency;
     lowEth = state.lowEth;

     await wallet.request({
       method: 'snap_manageState',
       params: ['update', state],
     });

     console.log({ notifToggle });
     if (notifToggle) {
       const currentData = await fetch(CURRENT_DATA_ENDPOINT, {
         method: 'get',
         headers: {
           'Content-Type': 'application/json',
         },
       });
       const current = await currentData.json();
       if (Math.abs(current.speeds[0].baseFee - lowEth) < urgency / 38) {
         return wallet.request({
           method: 'snap_notify',
           params: [
             {
               type: 'native',
               message: `Gasfee is low! Pay now at ${current.speeds[0].baseFee} Gwei`,
             },
           ],
         });
       }
       return wallet.request({
         method: 'snap_notify',
         params: [
           {
             type: 'native',
             message: `Current gasfee is ${current.speeds[0].baseFee} Gwei`,
           },
         ],
       });
     }
     return 0; // basically do nothing
   }

   default:
     throw new Error('Method not found.');
 }
};
Copiar

La configuración del cronjob correspondiente es (en snap.manifest.json):

"endowment:cronjob": {
      "jobs": [
        {
          "expression": {
            "minute": "*/3",
            "hour": "*",
            "dayOfMonth": "*",
            "month": "*",
            "dayOfWeek": "*"
          },
          "request": {
            "method": "exampleMethodOne",
            "params": {}
          }
        }
      ]
    }
Copiar

También existe una función de urgencia que establece la urgencia de la transacción (técnicamente, el parámetro que determina cuál debe ser la diferencia máxima entre la predicción y los precios actuales del gas para que el usuario proceda con la transacción). Modificar la urgencia varía este umbral. Implementamos la configuración de urgencia utilizando el método onRpcRequest del snap.


 export const onRpcRequest: OnRpcRequestHandler = async ({
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  origin,
  request,
}) => {
  switch (request.method) {
    // Other RPC methods here
    case 'set_urgency_35': {
      urgency = 35;

      let state: { notifToggle: boolean; urgency: number; lowEth: number };

      state = (await wallet.request({
        method: 'snap_manageState',
        params: ['get'],
      })) as { notifToggle: boolean; urgency: number; lowEth: number };

      if (!state) {
        state = {
          notifToggle,
          urgency: 35,
          lowEth,
        };
      }
      state.urgency = 35;

      await wallet.request({
        method: 'snap_manageState',
        params: ['update', state],
      });

      return urgency;
    }

    case 'set_urgency_60': {
      urgency = 60;

      let state: { notifToggle: boolean; urgency: number; lowEth: number };

      state = (await wallet.request({
        method: 'snap_manageState',
        params: ['get'],
      })) as { notifToggle: boolean; urgency: number; lowEth: number };

      if (!state) {
        state = {
          notifToggle,
          urgency: 60,
          lowEth,
        };
      }
      state.urgency = 60;

      await wallet.request({
        method: 'snap_manageState',
        params: ['update', state],
      });

      return urgency;
    }

    case 'set_urgency_90': {
      urgency = 90;

      let state: { notifToggle: boolean; urgency: number; lowEth: number };

      state = (await wallet.request({
        method: 'snap_manageState',
        params: ['get'],
      })) as { notifToggle: boolean; urgency: number; lowEth: number };

      if (!state) {
        state = {
          notifToggle,
          urgency: 90,
          lowEth,
        };
      }
      state.urgency = 90;

      await wallet.request({
        method: 'snap_manageState',
        params: ['update', state],
      });

      return urgency;
    }

    case 'set_urgency_100': {
      urgency = 100;

      let state: { notifToggle: boolean; urgency: number; lowEth: number };

      state = (await wallet.request({
        method: 'snap_manageState',
        params: ['get'],
      })) as { notifToggle: boolean; urgency: number; lowEth: number };

      if (!state) {
        state = {
          notifToggle,
          urgency: 100,
          lowEth,
        };
      }
      state.urgency = 100;

      await wallet.request({
        method: 'snap_manageState',
        params: ['update', state],
      });

      return urgency;
    }

    case 'call_api': {
      // get data from state whenever snap is invoked as snaps executions are ephemeral
      let state: { notifToggle: boolean; urgency: number; lowEth: number };

      state = (await wallet.request({
        method: 'snap_manageState',
        params: ['get'],
      })) as { notifToggle: boolean; urgency: number; lowEth: number };

      if (!state) {
        state = {
          notifToggle,
          urgency,
          lowEth,
        };
      }

      notifToggle = state.notifToggle;
      urgency = state.urgency;
      lowEth = state.lowEth;

      await wallet.request({
        method: 'snap_manageState',
        params: ['update', state],
      });

      console.log(
        `Called inside custom api call: notifToggle = ${notifToggle} and urgency = ${urgency}`,
      );
      const currentData = await fetch(CURRENT_DATA_ENDPOINT, {
        method: 'get',
        headers: {
          'Content-Type': 'application/json',
        },
      });
      const current = await currentData.json();
      if (Math.abs(current.speeds[0].baseFee - lowEth) < urgency / 38) {
        return wallet.request({
          method: 'snap_notify',
          params: [
            {
              type: 'native',
              message: `Gasfee is low! Pay now at ${current.speeds[0].baseFee} Gwei`,
            },
          ],
        });
      }
      return wallet.request({
        method: 'snap_notify',
        params: [
          {
            type: 'native',
            message: `Current gasfee is ${current.speeds[0].baseFee} Gwei`,
          },
        ],
      });
    }

    default: {
      throw new Error('Method not found.');
    }
  }
};
Copiar

Para calcular el umbral, dividimos el valor de urgencia entre 38. Tras pruebas empíricas con distintos valores, 38 resultó funcionar mejor en la mayoría de las situaciones. Está previsto ajustar y mejorar esto en un futuro próximo para lograr un rendimiento más dinámico.

if (Math.abs(current.speeds[0].baseFee - lowEth) < urgency / 38) {
          return wallet.request({
            method: 'snap_notify',
            params: [
              {
                type: 'native',
                message: `Gasfee is low! Pay now at ${current.speeds[0].baseFee} Gwei`,
              },
            ],
          });
        }
Copiar

Dado que los estados del snap son efímeros, necesitábamos almacenar la información de estado (como las notificaciones activadas/desactivadas y la urgencia establecida por el usuario) en el almacenamiento mediante la API de almacenamiento del snap.


export const onRpcRequest: OnRpcRequestHandler = async ({
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  origin,
  request,
}) => {
  switch (request.method) {
    case 'notif_toggle_true': {
      console.log('Notification toggled to true');
      notifToggle = true; // toggle the notification
      console.log({ notifToggle });

      let state: { notifToggle: boolean; urgency: number; lowEth: number };

      state = (await wallet.request({
        method: 'snap_manageState',
        params: ['get'],
      })) as { notifToggle: boolean; urgency: number; lowEth: number };

      if (!state) {
        state = {
          notifToggle: true,
          urgency,
          lowEth,
        };
      }
      state.notifToggle = true;

      await wallet.request({
        method: 'snap_manageState',
        params: ['update', state],
      });

      return notifToggle;
    }

    case 'notif_toggle_false': {
      console.log('Notification toggled to false');
      notifToggle = false; // toggle the notification
      console.log({ notifToggle });

      let state: { notifToggle: boolean; urgency: number; lowEth: number };

      state = (await wallet.request({
        method: 'snap_manageState',
        params: ['get'],
      })) as { notifToggle: boolean; urgency: number; lowEth: number };

      if (!state) {
        state = {
          notifToggle: false,
          urgency,
          lowEth,
        };
      }
      state.notifToggle = false;

      await wallet.request({
        method: 'snap_manageState',
        params: ['update', state],
      });

      return notifToggle;
    }

     default: {
      throw new Error('Method not found.');
    }
  }
}; 
Copiar

El Modelo

A continuación, utilizamos un popular modelo de predicción de series temporales llamado SARIMA o ARIMA Estacional del paquete statsforecast sobre los datos de series temporales para predecir el precio del gas más bajo esperado en la próxima hora.


from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import requests
import numpy as np
from statsforecast.models import AutoARIMA
import json

def get_timeseries():
    with open('key.json') as f:
        data = json.load(f)
        print(data)

    key = data['key']

    history_data = requests.get(
        'https://api.owlracle.info/v3/eth/history?apikey={}&candles=100&txfee=true'
        .format(key))
    history_data = history_data.json()

    low = []

    for obj in history_data:
        low.append(obj['gasPrice']['low'])

    low.reverse()

    return np.array(low)

def get_prediction():
    values = get_timeseries()
    model = AutoARIMA()
    predictions = model.forecast(values, 2)
    print("Pred: ", predictions)
    prediction_json = {
        'low_30_minutes': predictions['mean'][0],
        'low_60_minutes': predictions['mean'][1]
    }

    return prediction_json

app = FastAPI()

origins = [
    "*",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def root():
    pred = get_prediction()

    return pred
Copiar

¿Por qué SARIMA?

  • Numerosos estudios sobre SARIMA para la predicción de series temporales en distintos dominios indican que su rendimiento es bueno. También consultamos un estudio sobre estadísticas de precios del gas en Ethereum, donde se utilizó SARIMA y se obtuvieron resultados prometedores.

  • Una alternativa serían las técnicas de aprendizaje profundo, que generalmente implican modelos más grandes que también requieren más tiempo para volver a entrenarse, lo que los hace menos ideales para datos en streaming.

  • Algunas referencias consultadas: Un modelo híbrido SARIMA y máquinas de vectores de soporte para la predicción de los valores de producción de la industria maquinaria en Taiwán. Expert Systems with Applications, 32(1), 254-264. https://doi.org/10.1016/j.eswa.2005.11.027

  • Modelo lineal dinámico y SARIMA: una comparación de su rendimiento predictivo en epidemiología. https://doi.org/10.1002/sim.963

  • Estudio de escorrentía a largo plazo utilizando modelos SARIMA y ARIMA en los Estados Unidos. https://doi.org/10.1002/met.1491

  • Carl, David y Ewerhart, Christian, Ethereum Gas Price Statistics (22 de diciembre de 2020). Universidad de Zúrich, Departamento de Economía, Documento de Trabajo N.º 373, 2020. Disponible en: https://dx.doi.org/10.2139/ssrn.3754217

Frontend Web

Utilizamos una sencilla aplicación React como interfaz de frontend web. Contiene los botones para instalar el snap, enviar una transacción de prueba, configurar la urgencia del usuario, activar o desactivar las notificaciones de precios del gas y un selector de modo oscuro. Es muy sencilla y fácil de usar para cualquier usuario.

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

Hay mucho por hacer: podemos empezar por hacer el modelo más fiable ajustando el algoritmo de aprendizaje para obtener predicciones más robustas. Otra área de mejora es el umbral de urgencia, donde necesitamos desarrollar una expresión más dinámica que gestione simultáneamente las amplias variaciones del perfil de mercado, la urgencia del usuario y el ahorro en el gas de la transacción, llegando rápidamente a un punto casi óptimo para que el usuario realice el pago. Además, necesitamos hacer la interfaz web un poco más amigable para el usuario y añadir un manejo de errores general para algunos casos límite que pueden ocurrir raramente.

¿Puedes contarnos un poco sobre ti y tu equipo?

Somos un equipo formado por ocho estudiantes de ingeniería BTech en el IIT Patna. Formamos el equipo para participar en el Inter IIT Tech Meet 11 (2023), donde Consensys presentó un enunciado de problema relacionado con MetaMask Snaps.

  • Vaishakh: Soy estudiante de último año de Ciencias de la Computación en el IIT Patna y me apasiona mi campo de estudio. Últimamente he estado participando en hackathons y lo estoy disfrutando enormemente. Tengo muchas ganas de seguir explorando las vastas posibilidades de las Ciencias de la Computación y ver adónde me llevan en el futuro.

  • Aritra B.: Aritra es estudiante de segundo año de BTech en Inteligencia Artificial y Ciencia de Datos en el Instituto Indio de Tecnología de Patna, y le apasiona la blockchain y sus aplicaciones en la implementación masiva en el mundo real. Ha trabajado en varios proyectos pequeños de blockchain, lo que sentó las bases para aprender y explorar este campo. Su objetivo es crear soluciones inteligentes de blockchain combinando otros campos de las ciencias de la computación, como el aprendizaje automático, para generar nuevas innovaciones. Fue seleccionado como becario KVPY por el prestigioso Instituto Indio de Ciencias (IISC) en 2019 y actualmente está aprendiendo sobre ZK EVMs y sus áreas únicas de implementación en este espacio.

  • Padmaksh: Cursa BTech en Ingeniería Informática en el Instituto Indio de Tecnología de Patna y tiene un gran interés en la ciberseguridad. También le apasiona la resolución de problemas y los acertijos algorítmicos. Actualmente profundiza en la arquitectura blockchain y explora otros campos.

  • Kartikay: Estudiante de pregrado en el IIT Patna cursando B.Tech en Ingeniería Mecánica, actualmente en su tercer año. Tiene un gran interés en el desarrollo web y la programación competitiva. Ha diseñado y desarrollado múltiples sitios web para varios clubes y festivales universitarios. Tiene conocimientos básicos en Ciencia de Datos y Web3.

  • Harsh: Soy estudiante de segundo año de pregrado en el IIT Patna cursando Ingeniería Química. Me dedico principalmente a los estudios de caso y a la resolución de problemas.

  • Dhushyanth: Soy estudiante de último año de Ingeniería Eléctrica en el IIT Patna, muy entusiasta de la tecnología y el desarrollo. He incursionado en una variedad de campos, desde la implementación de servidores CRDT en Rust hasta el diseño de Perceptrones en Fotónica de Silicio. Me gusta tener conversaciones acaloradas sobre cualquier tema en el que pueda aportar mi opinión y lo disfruto mucho.

  • Vishwas S: Vishwas es estudiante de primer año de Ciencias de la Computación en el IIT Patna, apasionado por su campo de estudio y en busca de oportunidades para explorar. Recientemente ha desarrollado un gran interés por la tecnología blockchain y está trabajando en ella.

  • Ankur: Soy estudiante de segundo año de pregrado en el IIT Patna cursando Ingeniería Eléctrica y Electrónica. Recientemente desarrollé algunos sitios web para el IIT Patna y disfruto de la resolución de problemas. Últimamente me ha apasionado el UI y UX con especial inclinación por el frontend. Me gusta estudiar la psicología humana y trabajar en el desarrollo de soluciones a diversos problemas.

¿Cuándo conociste MetaMask Snaps por primera vez y cómo fue tu experiencia?

Conocimos MetaMask Snaps por primera vez en el Inter IIT Tech Meet 2023. Es un complemento realmente único e interesante para MetaMask, y puede utilizarse para aportar personalizaciones ilimitadas a la wallet. El proceso de desarrollo también es bastante sencillo y no encontramos grandes obstáculos durante el desarrollo, a pesar de que había poca documentación y contenido en foros de discusión disponibles. Aunque todavía está en desarrollo y actualmente carece de algunas funciones importantes, ya podemos ver el gran potencial que puede tener en los próximos años.

¿Qué diferencia a MetaMask Snaps de otras wallets?

MetaMask Snaps es similar a una extensión en un navegador web. Se utiliza para ampliar las funcionalidades de MetaMask. Es seguro, ya que los snaps se ejecutan en un entorno aislado donde tienen acceso a un conjunto limitado de capacidades determinadas por los permisos otorgados por el usuario durante la instalación. Es una forma excelente y segura de personalizar tu experiencia con la wallet.

Cuéntanos cómo es para ti y tu equipo el proceso de construir con MetaMask Snaps.

Es uno de los proyectos que nos permitió ver cómo diferentes campos de las ciencias de la computación pueden trabajar juntos para resolver un desafío. Para mi equipo y para mí, construir este snap fue una fantástica oportunidad de aprendizaje. Adquirimos conocimientos sobre cómo implementar nuestra idea de manera efectiva y también sobre cómo utilizar el aprendizaje automático para resolver un problema en blockchain. En el camino, también establecimos nuevas conexiones. Por otro lado, el resto de las wallets resultan bastante restrictivas para los desarrolladores.

¿Qué significa MetaMask Snaps para ti?

MetaMask Snaps añade las funciones de las extensiones a una wallet de criptomonedas. Es realmente innovador y puede utilizarse para muchos casos de uso críticos que mejoren la adopción masiva de las criptomonedas.

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

MetaMask Snaps puede ser realmente un punto de inflexión en el ecosistema Web3, ya que transforma una simple wallet de Ethereum en una herramienta completa de gestión Web3 donde podemos personalizar y mejorar nuestra experiencia en Web3.

¿Algún consejo que quieras compartir con los desarrolladores interesados en probar MetaMask Snaps? Empieza leyendo la documentación. Es muy explicativa y puede aclarar la mayoría de los casos de uso que uno pueda tener. Para cualquier problema cuya solución no sea evidente, te sugerimos que lo plantees en el Discord de Consensys, donde hay un foro dedicado a este propósito.

Construyendo con MetaMask Snaps

Para comenzar con MetaMask Snaps:

  1. Consulta la documentación para desarrolladores

  2. Instala MetaMask Flask

  3. Consulta una guía de MetaMask Snaps

  4. Mantente conectado con nosotros en Twitter, GitHub discussions 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 ni implícita, ya sea verbal o escrita, con respecto a los MetaMask Snaps desarrollados por terceros, y declina 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

Este artículo fue escrito por:

  • MetaMask

    MetaMask, anteriormente Consensys Software Inc, es la plataforma financiera self-custodial más grande del mundo, que ofrece a las personas un único lugar para guardar, gastar, ahorrar y hacer crecer su dinero tanto en criptoactivos como en activos tradicionales. La compañía está construyendo la plataforma de consumo donde esto ocurre, reuniendo pagos, ahorros, inversiones y activos digitales en una experiencia fluida y unificada. Habiendo crecido a partir de la wallet self-custodial más utilizada del mundo, MetaMask otorga a los usuarios control directo sobre su dinero y activos, con presencia en aproximadamente 190 países. MetaMask ha desempeñado un papel fundamental en el crecimiento de Ethereum desde 2016. Hoy, MetaMask se sitúa en el centro de la economía blockchain, construyendo el sistema operativo para el Open Money y poniendo a las personas en pleno control de su vida financiera.

    Leer todos los artículos