MetaMask Snaps — это путь к тому, чтобы сделать MetaMask самым расширяемым кошельком в мире. Как разработчик, вы можете добавлять свои функции и API в MetaMask совершенно новыми способами. Разработчики Web3 — это основа этого роста, и данная серия материалов призвана продемонстрировать новаторские MetaMask Snaps, создаваемые сегодня.
Цены на газ в блокчейне Ethereum крайне сложно предсказать, и это создаёт серьёзные неудобства при совершении платежей. Пользователь, пытающийся провести транзакцию, рискует заплатить значительно больше, чем необходимо. Кратковременные пики трафика в блокчейне могут резко влиять на цены на газ. Хотя L2-решения были созданы для снижения этих цен, иногда нужно более универсальное и простое решение, не требующее использования L2 для проведения транзакций. Мы решили, что пользователям будет полезно приложение, способное прогнозировать будущие цены на газ на основе исторических данных и текущих тенденций.
Можете рассказать о технической реализации?
EtherEyes состоит из четырёх частей: сервер цен на газ, snap, модель SARIMA и веб-интерфейс. Сервер цен на газ предоставляет данные об исторических и текущих ценах на газ. Веб-сайт размещает snap и предоставляет интерфейс для его установки и взаимодействия с ним через вызов методов snap API. Для прогнозирования цен на газ мы использовали модель сезонного авторегрессионного интегрированного скользящего среднего (SARIMA).
Сервер цен на газ
Мы собираем актуальные данные о ценах на газ в виде списка примерно из 200 свечей (каждая свеча представляет собой агрегат данных за последние 30 минут) с помощью Owlracle API. Это даёт нам временной ряд цен на газ за последние 100 часов.
Snap
Для snap мы использовали API анализа транзакций для предоставления прогнозов цен на газ в реальном времени. Процесс работы snap выглядит следующим образом:
Когда пользователь инициирует транзакцию, на странице подтверждения появляется блок анализа транзакции, реализованный с помощью onTransactionHandler. Здесь мы показываем пользователю прогноз на следующие 30 и 60 минут. Упрощённая версия нашей реализации представлена ниже:
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 };};
Копировать
Если пользователь решает подождать, он может активировать систему уведомлений с веб-сайта, которая взаимодействует со snap через обработчик onCronjob и отправляет цены на газ каждые три минуты в виде нативных браузерных уведомлений. Когда цены приближаются к прогнозируемому значению, пользователю предлагается завершить транзакцию.
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.');}};
Копировать
Соответствующие настройки cronjob (в snap.manifest.json):
Также предусмотрена функция срочности, которая задаёт степень срочности транзакции (технически — параметр, определяющий максимально допустимую разницу между прогнозом и текущими ценами на газ, при которой пользователю следует провести транзакцию). Изменение уровня срочности варьирует этот порог. Настройка срочности реализована с помощью метода snap onRpcRequest.
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.');}}};
Копировать
Для вычисления порога мы делим значение срочности на 38. После эмпирического тестирования с различными значениями выяснилось, что 38 лучше всего работает в большинстве ситуаций. В ближайшем будущем планируется доработка и улучшение этого параметра для более динамичной работы.
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`,},],});}
Копировать
Поскольку состояния snap являются эфемерными, нам потребовалось сохранять информацию о состоянии (например, включены/выключены уведомления, уровень срочности, заданный пользователем) в хранилище с помощью snap storage API.
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.');}}};
Копировать
Модель
Для прогнозирования минимальной цены на газ в течение следующего часа мы используем популярную модель прогнозирования временных рядов SARIMA (сезонная ARIMA) из пакета statsforecast.
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
Копировать
Почему SARIMA?
Многочисленные исследования по применению SARIMA для прогнозирования временных рядов в различных областях подтверждают высокую эффективность этой модели. Мы также обратились к исследованию статистики цен на газ в Ethereum, где использовалась SARIMA и были получены обнадёживающие результаты.
Альтернативой могли бы служить методы глубокого обучения, однако они, как правило, предполагают использование более крупных моделей, требующих больше времени на повторное обучение, что делает их менее подходящими для потоковых данных.
Использованные источники: гибридная модель SARIMA и метод опорных векторов для прогнозирования объёмов производства в машиностроительной отрасли Тайваня. Expert Systems with Applications, 32(1), 254–264. https://doi.org/10.1016/j.eswa.2005.11.027
Динамическая линейная модель и SARIMA: сравнение эффективности прогнозирования в эпидемиологии. https://doi.org/10.1002/sim.963
Carl, David and Ewerhart, Christian, Ethereum Gas Price Statistics (22 декабря 2020 г.). Университет Цюриха, Департамент экономики, Рабочий документ № 373, 2020, доступно по адресу: https://dx.doi.org/10.2139/ssrn.3754217
Веб-интерфейс
В качестве веб-интерфейса мы использовали простое React-приложение. Оно содержит кнопки для установки snap, отправки тестовой транзакции, настройки уровня срочности, переключения уведомлений о ценах на газ и переключения тёмного режима. Интерфейс очень прост и удобен для любого пользователя.
Каковы следующие шаги по развитию проекта?
Предстоит ещё многое сделать. Для начала можно повысить надёжность модели, скорректировав алгоритм обучения для получения более точных прогнозов. Ещё одна область для улучшения — порог срочности: необходимо разработать более динамичное выражение, способное одновременно учитывать широкие вариации рыночного профиля, уровня срочности пользователя и экономии на газе, быстро находя почти оптимальный момент для совершения платежа. Кроме того, нужно сделать веб-интерфейс более удобным и добавить общую обработку ошибок в редких граничных случаях.
Расскажите немного о себе и своей команде.
Мы — команда из восьми студентов бакалавриата технического направления (BTech) Индийского технологического института в Патне (IIT Patna). Команда была сформирована для участия в Inter IIT Tech Meet 11 (2023), где Consensys представила задачу, связанную с MetaMask Snaps.
Vaishakh: Я студент последнего курса по специальности «Информатика» в IIT Patna и очень люблю свою область. В последнее время я активно участвую в хакатонах и получаю от этого огромное удовольствие. Не терпится исследовать всё многообразие возможностей в информатике и посмотреть, куда это меня приведёт.
Aritra B.: Aritra — студент второго курса бакалавриата по направлению «Искусственный интеллект и наука о данных» в Индийском технологическом институте в Патне, увлечённый блокчейном и его применением в реальных массовых внедрениях. Он участвовал в ряде небольших блокчейн-проектов, которые заложили основу для изучения и исследования этой области. Его цель — создавать умные блокчейн-решения, используя другие области информатики, в частности машинное обучение, для создания новых инноваций. В 2019 году он был удостоен стипендии KVPY престижного Индийского института науки (IISc) и в настоящее время изучает ZK EVM и уникальные области их применения в этой сфере.
Padmaksh: Обучается по программе BTech в области компьютерных наук и инженерии в Индийском технологическом институте в Патне, проявляет живой интерес к кибербезопасности. Также очень увлечён решением задач и алгоритмическими головоломками. В настоящее время изучает архитектуру блокчейна и исследует другие области.
Kartikay: Студент бакалавриата IIT Patna по специальности «Машиностроение», в настоящее время на третьем курсе. Проявляет живой интерес к веб-разработке и спортивному программированию. Разработал несколько веб-сайтов для различных студенческих клубов и мероприятий. Имеет базовые знания в области Data Science и Web3.
Harsh: Я студент второго курса бакалавриата IIT Patna по специальности «Химическая инженерия». В основном занимаюсь кейс-стади и решением задач.
Dhushyanth: Я студент последнего курса по специальности «Электротехника» в IIT Patna, очень увлечённый технологиями и разработкой. Я пробовал себя в самых разных областях — от реализации CRDT-сервера на Rust до проектирования перцептронов в кремниевой фотонике. Люблю горячие дискуссии на любые темы, в которых могу высказать своё мнение, и получаю от этого огромное удовольствие!
Vishwas S: Vishwas — студент первого курса по специальности «Информатика» в IIT Patna, увлечённый своей областью и ищущий возможности для развития. В последнее время у него появился живой интерес к технологии блокчейн, и он активно работает в этом направлении.
Ankur: Я студент второго курса бакалавриата IIT Patna по специальности «Электротехника и электроника». Недавно разработал несколько веб-сайтов для IIT Patna и увлекаюсь решением задач. В последнее время меня всё больше привлекают UI и UX, особенно фронтенд. Люблю изучать психологию человека и работать над созданием решений для различных проблем.
Когда вы впервые познакомились с MetaMask Snaps и каким был этот опыт?
Мы впервые узнали о MetaMask Snaps на Inter IIT Tech Meet 2023. Это действительно уникальное и интересное дополнение к MetaMask, открывающее безграничные возможности для кастомизации кошелька. Процесс разработки также достаточно прямолинеен, и мы не столкнулись с серьёзными трудностями, несмотря на то что документации и материалов на форумах было не так много. Хотя продукт всё ещё находится в разработке и пока лишён ряда важных функций, уже сейчас очевиден его огромный потенциал в ближайшие годы.
Чем MetaMask Snaps отличается от других кошельков?
MetaMask Snaps похож на расширение в веб-браузере. Он используется для расширения функциональности MetaMask. Это безопасно, поскольку snaps работают в изолированной среде, где имеют доступ к ограниченному набору возможностей, определяемому разрешениями, предоставленными пользователем при установке. Это отличный и безопасный способ персонализировать работу с кошельком.
Расскажите, каково это — создавать MetaMask Snaps для вас и вашей команды?
Это один из проектов, который позволил нам увидеть, как различные области информатики могут работать вместе для решения одной задачи. Для меня и моей команды создание этого snap стало фантастической возможностью для обучения. Мы получили знания о том, как эффективно реализовать нашу идею, а также о том, как применять машинное обучение для решения задач в блокчейне. По ходу работы мы также завязали несколько новых знакомств. Что касается других кошельков, они кажутся весьма ограниченными для разработчиков.
Что для вас значит MetaMask Snaps?
MetaMask Snaps добавляет функциональность расширений в криптовалютный кошелёк. Это действительно инновационно и может применяться во многих важных сценариях для повышения массового принятия криптовалют.
Какие возможности вы видите в MetaMask Snaps и что это открывает для пространства Web3?
MetaMask Snaps может стать настоящим прорывом в экосистеме Web3, превращая простой Ethereum-кошелёк в полноценный инструмент управления Web3, где можно настраивать и улучшать свой опыт взаимодействия с децентрализованным вебом.
Есть ли советы для разработчиков, желающих попробовать MetaMask Snaps? Начните с изучения документации. Она очень подробная и охватывает большинство возможных сценариев использования. Если решение какой-либо проблемы не очевидно, рекомендуем задать вопрос в Discord Consensys, где есть специальный форум для этих целей.
Следите за нашей командой на следующем хакатоне в вашем регионе! Удачной разработки ⚒️
Отказ от ответственности: MetaMask Snaps, как правило, разрабатываются третьими сторонами, не являющимися Consensys Software. Использование MetaMask Snaps, разработанных третьими сторонами, осуществляется по вашему собственному усмотрению и на ваш риск, и вы соглашаетесь с тем, что несёте единоличную ответственность за любые убытки или ущерб, возникшие в результате такой деятельности. Consensys не даёт никаких явных или подразумеваемых гарантий, устных или письменных, в отношении MetaMask Snaps, разработанных третьими сторонами, и отказывается от какой-либо ответственности за Snaps, разработанные третьими сторонами. Использование программного обеспечения, связанного с блокчейном, сопряжено с рисками, которые вы полностью принимаете на себя при использовании MetaMask Snaps.
Подпишитесь на Alpha и получайте рыночную альфу прямо на почту
Переведено ИИ. Может содержать ошибки. Пожалуйста, всегда проверяйте информацию.
Оцените перевод
Автор статьи:
MetaMask
MetaMask, ранее известная как Consensys Software Inc, — крупнейшая в мире самостоятельная финансовая платформа, предоставляющая людям единое место для хранения, расходования, накопления и приумножения средств как в криптовалюте, так и в традиционных активах. Компания создаёт потребительскую платформу, объединяющую платежи, сбережения, инвестиции и цифровые активы в едином бесшовном опыте. Вырастая из самого широко используемого в мире self-custodial кошелька, MetaMask предоставляет пользователям прямой контроль над их деньгами и активами, охватывая около 190 стран. MetaMask сыграл основополагающую роль в развитии Ethereum с 2016 года. Сегодня MetaMask занимает центральное место в экономике блокчейна, создавая операционную систему для Open Money и предоставляя людям полный контроль над их финансовой жизнью.