MetaMask Snaps é o roteiro para tornar a MetaMask a carteira mais extensível do mundo. Como desenvolvedor, você pode trazer seus recursos e APIs para a MetaMask de maneiras totalmente novas. Os desenvolvedores Web3 são o núcleo desse crescimento, e esta série tem como objetivo destacar os novos MetaMask Snaps sendo construídos hoje.
Os preços de gas na blockchain Ethereum podem ser muito difíceis de prever e representam um obstáculo na hora de realizar pagamentos. Um usuário que tenta fazer pagamentos pode acabar pagando muito mais do que o necessário. Picos de tráfego de curta duração na blockchain podem causar um grande impacto nos preços de gas. Embora as L2s tenham sido criadas para reduzir esses preços, às vezes precisamos de uma solução mais geral e simples do que usar uma L2 para fins de transação. Achamos que seria útil para os usuários ter um aplicativo capaz de prever os preços futuros de gas com base em dados históricos e tendências atuais.
Você pode nos explicar a implementação técnica?
O EtherEyes é composto por quatro partes: servidor de preços de gas, o snap, o Modelo SARIMA e o frontend web. O servidor de preços de gas fornece dados sobre preços de gas históricos e atuais. O site hospeda o snap e oferece uma interface para instalá-lo e interagir com ele por meio de chamadas aos métodos da API do snap. Utilizamos o modelo SARIMA (Seasonal Autoregressive Integrated Moving Average — Média Móvel Integrada Autorregressiva Sazonal) para prever os preços de gas.
Servidor de Preços de Gas
Coletamos dados de preços de gas em tempo real como uma lista de cerca de 200 candlesticks (cada candlestick é um agregado dos últimos 30 minutos de dados) usando a API Owlracle. Isso nos fornece os dados de série temporal dos últimos 100 horas de preços de gas.
O Snap
Para o snap, utilizamos a API de transaction insights para fornecer previsões de preços de gas em tempo real. O fluxo do snap é o seguinte:
Primeiro, quando o usuário inicia uma transação, os transaction insights aparecem na página de confirmação, implementados por meio do onTransactionHandler. Aqui, mostramos ao usuário a previsão para os próximos 30 e 60 minutos. Uma versão simplificada do que implementamos é apresentada abaixo:
exportconstonTransaction:OnTransactionHandler=async({ transaction })=>{letstate:{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,};}letinsights: any ={type:'Gas Fee estimation',};if(!isObject(transaction)||!hasProperty(transaction,'data')||typeof transaction.data !=='string'){
console.warn('unknown transaction type');return{ insights };}const response =awaitfetch(`${MODEL_API_ENDPOINT}`,{method:'get',headers:{'Content-Type':'application/json',},});const currentData =awaitfetch(CURRENT_DATA_ENDPOINT,{method:'get',headers:{'Content-Type':'application/json',},});if(!response.ok){thrownewError('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
Se o usuário decidir aguardar, ele pode esperar e iniciar um sistema de notificações pelo site, que interage com o snap via handler onCronjob e envia os preços de gas a cada três minutos como notificações nativas do navegador. Quando os preços se aproximam bastante da previsão, o sistema solicita que o usuário conclua a transação.
exportconstonCronjob:OnCronjobHandler=async({ request })=>{switch(request.method){case'exampleMethodOne':{// get data from state whenever snap is invoked as snaps executions are ephemeralletstate:{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 =awaitfetch(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`,},],});}return0;// basically do nothing}default:thrownewError('Method not found.');}};
Copiar
As configurações de cronjob correspondentes estão em (snap.manifest.json):
Há também um recurso de urgência que define o nível de urgência da transação (tecnicamente, o parâmetro que determina qual deve ser a diferença máxima entre a previsão e os preços de gas atuais para que o usuário prossiga com a transação). Alterar a urgência varia esse limite. Implementamos a configuração de urgência usando o método onRpcRequest do snap.
exportconstonRpcRequest: OnRpcRequestHandler =async({// eslint-disable-next-line @typescript-eslint/no-unused-vars
origin,
request,})=>{switch(request.method){// Other RPC methods herecase'set_urgency_35':{
urgency =35;letstate:{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;letstate:{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;letstate:{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;letstate:{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 ephemeralletstate:{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 =awaitfetch(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:{thrownewError('Method not found.');}}};
Copiar
Para calcular o limite, dividimos a configuração de urgência por 38. Após testes empíricos com diferentes valores, 38 pareceu funcionar melhor na maioria das situações. Há planos para ajustar e aprimorar isso em breve para um desempenho mais 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
Como os estados do snap são efêmeros, precisamos armazenar informações de estado (como notificação ativada/desativada e urgência definida pelo usuário) no armazenamento usando a API de storage do snap.
exportconstonRpcRequest: 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 });letstate:{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 });letstate:{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:{thrownewError('Method not found.');}}};
Copiar
O Modelo
Em seguida, utilizamos um popular modelo de previsão de séries temporais chamado SARIMA ou ARIMA Sazonal, do pacote statsforecast, aplicado aos dados de série temporal para prever o menor preço de gas esperado na 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():withopen('key.json')asf:
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 inhistory_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 que SARIMA?
Diversas publicações sobre SARIMA para previsão de séries temporais em diferentes domínios indicam que seu desempenho é satisfatório. Também consultamos um estudo sobre estatísticas de preços de gas no Ethereum, no qual o SARIMA foi utilizado e apresentou resultados promissores.
Uma alternativa seriam técnicas de deep learning, que geralmente envolvem modelos maiores e demandam mais tempo para retreinamento, tornando-as menos ideais para dados em streaming.
Algumas publicações consultadas: - Um modelo híbrido SARIMA e máquinas de vetores de suporte na previsão dos valores de produção da indústria de máquinas em Taiwan. Expert Systems with Applications, 32(1), 254-264. https://doi.org/10.1016/j.eswa.2005.11.027
Modelo linear dinâmico e SARIMA: uma comparação de seu desempenho de previsão em epidemiologia. https://doi.org/10.1002/sim.963
Carl, David e Ewerhart, Christian, Ethereum Gas Price Statistics (22 de dezembro de 2020). Universidade de Zurique, Departamento de Economia, Working Paper nº 373, 2020, disponível em: https://dx.doi.org/10.2139/ssrn.3754217
Frontend Web
Utilizamos um aplicativo React simples como interface de frontend web. Ele contém botões para instalar o snap, enviar uma transação fictícia, definir a urgência do usuário, ativar/desativar notificações de preços de gas e alternar o modo escuro. É muito simples e fácil de usar para qualquer usuário.
Quais são os próximos passos para implementar isso?
Há muito que pode ser feito: podemos começar tornando o modelo mais confiável, ajustando o algoritmo de aprendizado para gerar previsões mais robustas. Outra área a melhorar é o limite de urgência, onde precisamos desenvolver uma expressão mais dinâmica para lidar com amplas variações de perfil de mercado, urgência do usuário e economia de gas em transações ao mesmo tempo, chegando rapidamente a um ponto quase ideal para que o usuário realize o pagamento. Também precisamos tornar a interface web um pouco mais amigável e adicionar tratamento de erros em alguns casos extremos que podem ocorrer raramente.
Você pode nos contar um pouco sobre você e sua equipe?
Somos uma equipe composta por oito estudantes de BTech em engenharia no IIT Patna. Formamos a equipe para participar do Inter IIT Tech Meet 11 (2023), onde a Consensys apresentou um desafio envolvendo MetaMask Snaps.
Vaishakh: Sou estudante do último ano de Ciência da Computação no IIT Patna e adoro minha área de estudo. Ultimamente, tenho participado de hackathons e estou curtindo muito a experiência. Mal posso esperar para explorar mais as vastas possibilidades da Ciência da Computação e ver onde isso me levará no futuro.
Aritra B.: Aritra é estudante de segundo ano de BTech em IA e Ciência de Dados no Indian Institute of Technology, Patna, e é apaixonado por Blockchain e suas aplicações na implementação em massa no mundo real. Ele trabalhou em vários projetos pequenos de blockchain antes, o que lançou as bases para aprender e explorar esse domínio. Seu objetivo é criar soluções inteligentes de blockchain usando outras áreas da ciência da computação, como machine learning, para gerar novas inovações. Ele foi selecionado como bolsista KVPY pelo prestigioso Indian Institute of Science (IISC) em 2019 e atualmente está aprendendo sobre ZK EVMs e suas áreas únicas de implementação nesse espaço.
Padmaksh: Cursando BTech em Ciência da Computação e Engenharia no Indian Institute of Technology Patna, tem grande interesse em segurança cibernética. Também é muito interessado em resolução de problemas e puzzles algorítmicos. Está aprofundando seus estudos em arquitetura blockchain e explorando outras áreas.
Kartikay: Graduando no IIT Patna cursando B.Tech em Engenharia Mecânica, atualmente no 3º ano do curso. Tem grande interesse em desenvolvimento web e programação competitiva. Projetou e desenvolveu múltiplos sites para vários clubes e festivais da faculdade. Possui conhecimentos básicos em Data Science e Web3.
Harsh: Sou estudante de segundo ano de graduação no IIT Patna cursando Engenharia Química. Sou bastante voltado para estudos de caso e resolução de problemas.
Dhushyanth: Sou estudante do último ano de Engenharia Elétrica no IIT Patna, muito entusiasmado com tecnologia e desenvolvimento. Já me aventurei em diversas áreas, desde implementação de servidor CRDT em Rust até o design de Perceptrons em Fotônica de Silício. Gosto de debates acalorados sobre qualquer assunto em que possa contribuir e aproveito muito isso!
Vishwas S: Vishwas é estudante do primeiro ano de Ciência da Computação no IIT Patna, apaixonado pela sua área de estudo e em busca de oportunidades para explorar. Recentemente, desenvolveu grande interesse pela tecnologia blockchain e está trabalhando com ela.
Ankur: Sou estudante de segundo ano de graduação no IIT Patna cursando Engenharia Elétrica e Eletrônica. Recentemente desenvolvi alguns sites para o IIT Patna e gosto de resolver problemas. Ultimamente tenho me dedicado a UI e UX com foco em frontend. Gosto de estudar psicologia humana e trabalhar no desenvolvimento de soluções para diversos problemas.
Quando você teve seu primeiro contato com o MetaMask Snaps e como foi essa experiência?
Tivemos nosso primeiro contato com o MetaMask Snaps no Inter IIT Tech Meet 2023. É um complemento realmente único e interessante para a MetaMask, e pode ser usado para trazer personalizações infinitas à carteira. O processo de desenvolvimento também é bastante direto e não enfrentamos grandes obstáculos durante o desenvolvimento, mesmo com pouca documentação e conteúdo em fóruns de discussão disponíveis. Embora ainda esteja em desenvolvimento e careça de alguns recursos importantes por enquanto, já podemos ver o grande potencial que terá nos próximos anos.
O que diferencia o MetaMask Snaps de outras carteiras?
O MetaMask Snaps é semelhante a uma extensão em um navegador web. Ele é usado para ampliar as funcionalidades da MetaMask. É seguro, pois os snaps são executados em um ambiente isolado onde têm acesso a um conjunto limitado de capacidades determinadas pelas permissões concedidas pelo usuário durante a instalação. É uma forma excelente e segura de personalizar sua experiência com a carteira.
Conte-nos como é construir com o MetaMask Snaps para você e sua equipe.
É um dos projetos que nos permitiu ver como diferentes áreas da ciência da computação podem trabalhar juntas para resolver um desafio. Para mim e minha equipe, construir este snap foi uma fantástica oportunidade de aprendizado. Adquirimos conhecimento sobre como implementar nossa ideia de forma eficaz, bem como sobre como usar machine learning para resolver um problema em blockchain. Ao longo do caminho, também estabelecemos algumas novas conexões. Por outro lado, as demais carteiras parecem bastante restritivas para os desenvolvedores.
O que o MetaMask Snaps significa para você?
O MetaMask Snaps adiciona as funcionalidades de extensões a uma carteira de criptomoedas. É realmente inovador e pode ser usado em muitos casos de uso críticos para impulsionar a adoção em massa de criptomoedas.
Que oportunidades você enxerga com o MetaMask Snaps e as possibilidades que ele abre para o espaço Web3?
O MetaMask Snaps pode realmente ser um avanço no ecossistema Web3, pois transforma uma simples carteira Ethereum em uma ferramenta completa de gestão Web3, onde podemos personalizar e aprimorar nossa experiência Web3.
Algum conselho que você gostaria de compartilhar com desenvolvedores interessados em experimentar o MetaMask Snaps? Comece lendo a documentação. Ela é muito explicativa e abrange a maioria dos casos de uso possíveis. Para qualquer problema cuja solução não seja evidente, sugerimos perguntar no Discord da Consensys, onde há um fórum dedicado a esse propósito.
Fique de olho em nossa equipe no próximo hackathon perto de você! Bom BUIDLing ⚒️
Aviso legal: Os MetaMask Snaps são geralmente desenvolvidos por terceiros que não a Consensys Software. O uso de MetaMask Snaps desenvolvidos por terceiros é feito a seu próprio critério e risco, com a concordância de que você será o único responsável por qualquer perda ou dano resultante de tais atividades. A Consensys não oferece nenhuma garantia expressa ou implícita, seja verbal ou escrita, em relação a MetaMask Snaps desenvolvidos por terceiros e se isenta de toda responsabilidade por Snaps desenvolvidos por terceiros. O uso de software relacionado a blockchain envolve riscos, e você os assume integralmente ao usar o MetaMask Snaps.
Assine Alpha e receba o alpha de mercado direto na sua caixa de entrada
Traduzido por IA. Pode conter erros. Por favor, verifique sempre as informações.
Avalie a tradução
Este artigo foi escrito por:
MetaMask
A MetaMask, anteriormente Consensys Software Inc, é a maior plataforma financeira self-custodial do mundo, oferecendo às pessoas um único lugar para guardar, gastar, economizar e fazer seu dinheiro crescer em ativos cripto e tradicionais. A empresa está construindo a plataforma de consumo onde isso acontece, reunindo pagamentos, poupança, investimentos e ativos digitais em uma experiência integrada e fluida. Tendo crescido a partir da carteira self-custodial mais amplamente utilizada do mundo, a MetaMask oferece aos usuários controle direto sobre seu dinheiro e ativos, com alcance em aproximadamente 190 países. A MetaMask desempenhou um papel fundamental no crescimento do Ethereum desde 2016. Hoje, a MetaMask está no centro da economia blockchain, construindo o sistema operacional para o Open Money e colocando as pessoas em total controle de suas vidas financeiras.