MetaMask Snaps는 MetaMask를 세계에서 가장 확장성 높은 지갑으로 만들기 위한 로드맵입니다. 개발자라면 완전히 새로운 방식으로 자신만의 기능과 API를 MetaMask에 구현할 수 있습니다. Web3 개발자들은 이러한 성장의 핵심이며, 이 시리즈는 현재 개발 중인 혁신적인 MetaMask Snaps를 소개하는 것을 목표로 합니다.
이더리움 블록체인의 가스 가격은 예측하기가 매우 어렵고, 결제를 진행할 때 걸림돌이 되곤 합니다. 사용자가 결제를 시도할 때 필요 이상으로 많은 금액을 지불하게 될 수 있습니다. 블록체인의 단기적인 트래픽 급증은 가스 가격에 큰 영향을 미칠 수 있습니다. L2가 가스 가격을 낮추기 위해 개발되었지만, 트랜잭션 목적으로 L2를 사용하는 것보다 더 범용적이고 간편한 솔루션이 필요할 때도 있습니다. 저희는 과거 데이터와 현재 트렌드를 기반으로 미래 가스 가격을 예측할 수 있는 앱이 사용자에게 유용할 것이라고 생각했습니다.
기술적 구현 과정을 설명해 주실 수 있나요?
EtherEyes는 가스 가격 서버, Snap, SARIMA 모델, 웹 프론트엔드의 네 가지 부분으로 구성됩니다. 가스 가격 서버는 과거 및 현재 가스 가격 데이터를 제공합니다. 웹사이트는 Snap을 호스팅하고 Snap API 메서드를 호출하여 Snap을 설치하고 상호작용할 수 있는 인터페이스를 제공합니다. 가스 가격 예측에는 계절성 자기회귀 누적 이동평균(SARIMA) 모델을 사용했습니다.
가스 가격 서버
Owlracle API를 사용하여 약 200개의 캔들스틱 목록(각 캔들스틱은 과거 30분간의 데이터 집계) 형태로 실시간 가스 가격 데이터를 수집합니다. 이를 통해 과거 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 };};
복사
사용자가 기다리기로 결정한 경우, onCronjob 핸들러를 통해 Snap과 상호작용하는 웹사이트에서 알림 시스템을 시작하고 3분마다 네이티브 브라우저 알림으로 가스 가격을 전송할 수 있습니다. 가격이 예측값에 매우 근접하면 사용자에게 트랜잭션을 완료하도록 안내합니다.
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.');}};
또한 트랜잭션의 긴급도를 설정하는 긴급도 기능도 있습니다(기술적으로는 예측값과 현재 가스 가격 간의 최대 허용 차이를 결정하는 파라미터로, 이 값에 도달하면 사용자가 트랜잭션을 진행해야 함을 의미합니다). 긴급도를 변경하면 이 임계값이 달라집니다. 긴급도 설정은 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 스토리지 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.');}}};
복사
모델
statsforecast 패키지의 시계열 데이터에 SARIMA(계절성 ARIMA)라는 널리 사용되는 시계열 예측 모델을 적용하여 다음 한 시간 내에 예상되는 최저 가스 가격을 예측합니다.
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 관련 문헌들에서 SARIMA의 성능이 우수하다고 언급하고 있습니다. 또한 이더리움 가스 가격 통계에 관한 연구를 참고했는데, 해당 연구에서 SARIMA를 사용하여 유망한 결과를 얻었습니다.
대안으로는 딥러닝 기법이 있으나, 일반적으로 더 큰 모델을 필요로 하고 재학습에 더 많은 시간이 소요되어 스트리밍 데이터에는 적합하지 않습니다.
Carl, David and Ewerhart, Christian, Ethereum Gas Price Statistics (2020년 12월 22일). University of Zurich, Department of Economics, Working Paper No. 373, 2020, 다음에서 확인 가능: https://dx.doi.org/10.2139/ssrn.3754217
웹 프론트엔드
웹 프론트엔드 인터페이스로 간단한 React 앱을 사용했습니다. Snap 설치, 더미 트랜잭션 전송, 사용자 긴급도 설정, 가스 가격 알림 토글, 다크 모드 토글 버튼이 포함되어 있습니다. 모든 사용자가 매우 간단하고 쉽게 사용할 수 있습니다.
이것을 구현하기 위한 다음 단계는 무엇인가요?
할 수 있는 것들이 많습니다. 먼저 학습 알고리즘을 조정하여 더 견고한 예측을 할 수 있도록 모델의 신뢰성을 높이는 것부터 시작할 수 있습니다. 또 다른 개선 영역은 긴급도 임계값으로, 다양한 시장 프로필, 사용자 긴급도, 트랜잭션 가스 절감을 동시에 처리하고 사용자가 결제해야 할 거의 최적의 시점에 빠르게 도달할 수 있는 더 동적인 표현식을 개발해야 합니다. 또한 웹 인터페이스를 좀 더 사용자 친화적으로 만들고 드물게 발생할 수 있는 일부 엣지 케이스에 대한 전반적인 오류 처리를 추가해야 합니다.
본인과 팀에 대해 간략히 소개해 주시겠어요?
저희는 IIT 파트나의 BTech 공학과 학생 8명으로 구성된 팀입니다. Consensys가 MetaMask Snaps 문제를 제시한 Inter IIT Tech Meet 11(2023)에 참가하기 위해 팀을 구성했습니다.
Vaishakh: 저는 IIT 파트나의 컴퓨터 공학과 최종 학년 학생으로 제 전공을 매우 좋아합니다. 최근에는 해커톤에 참가하고 있으며 그 경험을 정말 즐기고 있습니다. 컴퓨터 공학의 광대한 가능성을 더 탐구하고 미래에 어디로 이어질지 기대됩니다.
Aritra B.: Aritra는 인도공과대학교 파트나의 AI 및 데이터 사이언스 학과 BTech 2학년 학생으로, 블록체인과 실제 대중 구현에서의 응용에 열정을 가지고 있습니다. 이전에 여러 소규모 블록체인 프로젝트에 참여하여 이 분야를 학습하고 탐구하는 기반을 마련했습니다. 머신러닝과 같은 컴퓨터 공학의 다른 분야를 활용하여 새로운 혁신을 창출하는 스마트 블록체인 솔루션을 만드는 것을 목표로 하고 있습니다. 2019년 인도과학원(IISC)의 권위 있는 KVPY 펠로우로 선발되었으며, 현재 ZK EVM과 이 분야에서의 독특한 구현 영역에 대해 학습하고 있습니다.
Padmaksh: 인도공과대학교 파트나에서 컴퓨터 공학 BTech를 전공하고 있으며 사이버 보안에 깊은 관심을 가지고 있습니다. 문제 해결과 알고리즘 퍼즐에도 매우 관심이 많습니다. 현재 블록체인 아키텍처를 더 공부하고 다른 분야를 탐구하고 있습니다.
Kartikay: IIT 파트나에서 기계공학 B.Tech를 전공하는 3학년 학부생입니다. 웹 개발과 경쟁 프로그래밍에 깊은 관심을 가지고 있습니다. 여러 대학 동아리와 축제를 위한 다수의 웹사이트를 설계하고 개발했습니다. 데이터 사이언스와 Web3에 대한 기본 지식을 보유하고 있습니다.
Harsh: 저는 IIT 파트나에서 화학공학을 전공하는 2학년 학부생입니다. 주로 케이스 스터디와 문제 해결에 관심이 있습니다.
Dhushyanth: 저는 IIT 파트나의 전기공학과 최종 학년 학생으로 기술과 개발에 매우 열정적입니다. Rust에서의 CRDT 서버 구현부터 실리콘 포토닉스에서의 퍼셉트론 설계까지 다양한 분야를 경험했습니다. 제 의견을 나눌 수 있는 어떤 주제에 대해서도 열띤 토론을 즐깁니다!
Vishwas S: Vishwas는 IIT 파트나의 컴퓨터 공학과 1학년 학생으로 자신의 전공에 열정을 가지고 탐구할 기회를 찾고 있습니다. 최근 블록체인 기술에 깊은 관심을 갖게 되어 이 분야에서 활동하고 있습니다.
Ankur: 저는 IIT 파트나에서 전기전자공학을 전공하는 2학년 학부생입니다. 최근 IIT 파트나를 위한 몇 가지 웹사이트를 개발했으며 문제 해결을 즐깁니다. 최근에는 프론트엔드에 대한 감각과 함께 UI 및 UX를 즐기고 있습니다. 인간 심리학을 공부하고 다양한 문제에 대한 솔루션을 구축하는 것을 좋아합니다.
MetaMask Snaps를 처음 접한 것은 언제이며, 어떤 경험이었나요?
저희는 Inter IIT Tech Meet 2023에서 MetaMask Snaps를 처음 접했습니다. MetaMask에 정말 독특하고 흥미로운 부가 기능으로, 지갑에 무한한 커스터마이징을 가져올 수 있습니다. 개발 과정도 꽤 간단하여 문서와 토론 포럼 콘텐츠가 부족했음에도 불구하고 개발 중에 큰 어려움을 겪지 않았습니다. 아직 개발 중이며 현재 몇 가지 중요한 기능이 부족하지만, 앞으로 몇 년 안에 가질 수 있는 엄청난 잠재력을 이미 확인할 수 있습니다.
MetaMask Snaps가 다른 지갑과 다른 점은 무엇인가요?
MetaMask Snaps는 웹 브라우저의 확장 프로그램과 유사합니다. MetaMask의 기능을 확장하는 데 사용됩니다. Snap은 설치 시 사용자가 부여한 권한에 의해 결정된 제한된 기능 집합에만 접근할 수 있는 격리된 환경에서 실행되므로 안전합니다. 지갑 경험을 커스터마이징하는 훌륭하고 안전한 방법입니다.
팀과 함께 MetaMask Snaps를 구축하는 경험은 어떠했나요?
이 프로젝트는 다양한 컴퓨터 공학 분야가 함께 협력하여 문제를 해결할 수 있다는 것을 직접 목격하게 해준 프로젝트 중 하나였습니다. 저희 팀에게 이 Snap을 구축하는 것은 훌륭한 학습 기회였습니다. 아이디어를 효과적으로 구현하는 방법과 머신러닝을 활용하여 블록체인 문제를 해결하는 방법에 대한 지식을 얻었습니다. 그 과정에서 몇 가지 새로운 인연도 맺었습니다. 반면, 다른 지갑들은 개발자에게 상당히 제한적으로 느껴집니다.
MetaMask Snaps는 여러분에게 어떤 의미인가요?
MetaMask Snaps는 크립토 지갑에 확장 프로그램의 기능을 추가합니다. 정말 혁신적이며 암호화폐의 대중 채택을 개선하기 위한 많은 중요한 사용 사례에 활용될 수 있습니다.
MetaMask Snaps가 Web3 공간에서 열어주는 가능성과 기회는 무엇이라고 생각하시나요?
MetaMask Snaps는 단순한 이더리움 지갑을 Web3 경험을 커스터마이징하고 향상시킬 수 있는 완전한 Web3 관리 도구로 변환시키기 때문에 Web3 생태계에서 진정한 돌파구가 될 수 있습니다.
MetaMask Snaps를 시도하려는 개발자들에게 조언이 있으신가요?문서를 읽는 것부터 시작하세요. 매우 상세하게 설명되어 있으며 대부분의 사용 사례를 설명해 줍니다. 해결책이 명확하지 않은 문제가 있다면, 이를 위한 전용 포럼이 있는 Consensys 디스코드에서 질문하는 것을 권장합니다.
면책 조항: MetaMask Snaps는 일반적으로 Consensys Software 이외의 제3자에 의해 개발됩니다. 제3자가 개발한 MetaMask Snaps의 사용은 전적으로 본인의 판단과 위험 부담 하에 이루어지며, 그러한 활동으로 인한 손실이나 피해에 대해 전적으로 책임을 진다는 것에 동의하는 것입니다. Consensys는 제3자가 개발한 MetaMask Snaps에 대해 구두 또는 서면을 불문하고 명시적 또는 묵시적 보증을 하지 않으며, 제3자가 개발한 Snaps에 대한 모든 책임을 부인합니다. 블록체인 관련 소프트웨어 사용에는 위험이 따르며, MetaMask Snaps를 사용함으로써 해당 위험을 전적으로 감수하게 됩니다.
Alpha를 구독하고 시장 알파를 이메일로 바로 받아보세요
AI가 번역했습니다. 오류가 있을 수 있습니다. 항상 정보를 확인하시기 바랍니다.
번역 평가하기
이 글의 작성자:
MetaMask
MetaMask(구 Consensys Software Inc)은 세계 최대의 자기 수탁형 금융 플랫폼으로, 암호화폐와 전통 자산 모두에 걸쳐 자산을 보유하고, 사용하고, 저축하고, 불릴 수 있는 단일 공간을 제공합니다. MetaMask는 결제, 저축, 투자, 디지털 자산을 하나의 seamless한 경험으로 통합하는 소비자 플랫폼을 구축하고 있습니다. 세계에서 가장 널리 사용되는 자기 수탁형 지갑에서 성장한 MetaMask는 약 190개국에 걸쳐 사용자가 자신의 자금과 자산을 직접 통제할 수 있도록 합니다. MetaMask는 2016년부터 이더리움의 성장에 핵심적인 역할을 해왔습니다. 오늘날 MetaMask는 블록체인 경제의 중심에 자리하며, 오픈 머니(Open Money)를 위한 운영 체제를 구축하고 사람들이 자신의 금융 생활을 완전히 주도할 수 있도록 하고 있습니다.