İçeriğe geç

Etkinlik Biletleme Dapp'inde MetaMask Gas API Nasıl Uygulanır

Bu blogda, Next.js ve ShadcnUI ile oluşturulmuş mevcut bir biletleme dApp'ine MetaMask Gas API'nin nasıl entegre edileceğini inceledik.

6 dk okuma
Etkinlik Biletleme Dapp'inde MetaMask Gas API Nasıl Uygulanır

Blok zinciri tabanlı etkinlik biletleri satın almadan önce gas fiyatını anlamak, hem maliyet şeffaflığı hem de etkili finansal planlama açısından kritik öneme sahiptir. Blok zinciri işlemleri bağlamında "gas", blok zincirinde bir işlemi başarıyla gerçekleştirmek veya bir sözleşmeyi yürütmek için gereken ücreti ifade eder.

Bu ücret, ağ talebine bağlı olarak dalgalanır. Gas fiyatını önceden bilerek bilet alıcıları, satın alma işlemlerinin toplam maliyeti hakkında daha bilinçli kararlar verebilir. Bu durum, özellikle ağ yoğunluğunun yüksek olduğu dönemlerde gas ücretinin bilet toplam maliyetine önemli ölçüde eklenebildiği göz önüne alındığında büyük önem taşır.

Bu eğitimde, kullanıcıların etkinlik biletleri satın alırken gas fiyatlarını görebilmesini sağlamak amacıyla MetaMask Gas API'sini mevcut bir etkinlik biletleme dapp'ine entegre edeceğiz.

Ön koşullar ve bağımlılıklar

Frontend framework olarak Next.js, kullanıcı arayüzü için ise ShadcnUI kullanacağız. Aşağıdaki ShadcnUI bileşenlerini yükleyelim:

npx shadcn-ui@latest add hover-card
npm i swr
Kopyala

Yukarıdaki kod, gas fiyatlarını görüntülemek için kullanacağımız hover-card bileşenini ekleyecek ve SWR React hooks kütüphanesini kuracaktır. Stale-While-Revalidate (SWR) kütüphanesi, internetten veri çekme işlemini kolaylaştırır. Önce kaydettiği eski verileri kullanır, ardından yeni veri talep eder ve son olarak eski verileri bu yeni bilgilerle değiştirir. Bu sayede web siteleri hem daha hızlı hem de daha güncel hale gelir.

Ardından, web uygulamalarında görevlerin ilerleme durumunu veya yükleme hallerini göstermek için kullanışlı olan dairesel ilerleme göstergelerini görüntülemek amacıyla react-circular-progressbar bileşenini yükleyeceğiz. Bu bileşen özelleştirilebilir yapıda olup yüksek kaliteli grafikler için SVG kullanılarak uygulanmıştır.

npm i react-circular-progressbar

Gas API'sinin Uygulanması

app/web dizininde şu klasörü oluşturalım: app/api/gas. İçine bir route.ts dosyası oluşturup aşağıdaki kodu ekleyelim:

import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const chainId = searchParams.get("chainId");

  const Auth = Buffer.from(
    process.env.INFURA_API_KEY + ":" + process.env.INFURA_API_SECRET
  ).toString("base64");

  const gasPricesResp = await fetch(
    `https://gas.api.infura.io/networks/${chainId}/suggestedGasFees`,
    {
      headers: {
        Authorization: `Basic ${Auth}`,
      },
    }
  );

  const baseFeePercentileRes = await fetch(
    `https://gas.api.infura.io/networks/${chainId}/baseFeePercentile`,
    {
      headers: {
        Authorization: `Basic ${Auth}`,
      },
    }
  );

  const gasPricesData = await gasPricesResp.json();
  const baseFeePercentileData = await baseFeePercentileRes.json();

  return NextResponse.json({
    estimatedBaseFee: gasPricesData.estimatedBaseFee,
    baseFeeTrend: gasPricesData.baseFeeTrend,
    baseFeePercentile: baseFeePercentileData.baseFeePercentile,
  });
}
Kopyala

Burada neler yaptığımıza bakalım:

  1. Sorgu Parametrelerinin Çıkarılması:- const { searchParams } = new URL(request.url);: İstek URL'sinden arama parametrelerini çıkarır.

  • const chainId = searchParams.get("chainId");: Ethereum ağını belirtmek için kullanılan chainId parametresini URL'den alır.

  1. Yetkilendirmenin Ayarlanması:- INFURA_API_KEY ve INFURA_API_SECRET ortam değişkenleri bir Buffer içinde birleştirilerek Base64 dizesine dönüştürülür. Bu dize, Infura API'sine istek gönderilirken temel HTTP kimlik doğrulaması için kullanılır.

  2. Gas Fiyatları ve Temel Ücret Yüzdelik Dilim Verilerinin Çekilmesi:- Infura API'sine iki eş zamansız fetch isteği gönderilir:- gasPricesResp, belirtilen Ethereum ağı (chainId) için önerilen gas ücretlerini çeker.

  • baseFeePercentileRes, aynı ağ için temel ücret yüzdelik dilimini çeker.

  • Authorization başlığı, Base64 ile kodlanmış kimlik bilgileriyle ayarlanır.

  1. Verilerin Ayrıştırılması ve Döndürülmesi:- Her iki fetch isteğinden gelen yanıtlar JSON'a dönüştürülür.

  • NextResponse.json({...}) aşağıdaki verileri içeren bir JSON yanıtı oluşturur ve döndürür:- estimatedBaseFee: İşlemler için tahmini temel ücret.

  • baseFeeTrend: Temel ücretin eğilimi (örneğin artış veya azalış).

  • baseFeePercentile: Temel ücretlerin yüzdelik dilim dağılımı.

Kullanıcı Arayüzünün Oluşturulması

GasFeeCard.tsx adında bir bileşen oluşturup içine aşağıdaki kodu ekleyeceğiz:


import useSWR, { mutate } from "swr";
import {
  CircularProgressbarWithChildren,
  buildStyles,
} from "react-circular-progressbar";
import "react-circular-progressbar/dist/styles.css";
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card";
import { useEffect, useState } from "react";
import { ArrowUp, ArrowDown, InfoIcon } from "lucide-react";
import { useSDK } from "@metamask/sdk-react";

const REFETCH_INTERVAL = 10000;
const STEP_INTERVAL = 1000;
const NETWORK_LATENCY = 2000; // rough estimate of network latency to consider when refetching

// format the gas price nicely for small values
const formatGweiValues = (value: string) => {
  const gwei = parseFloat(value);

  if (gwei < 0.01) {
    return gwei.toExponential(2);
  } else {
    return gwei.toFixed(2);
  }
};

export function GasFeeCard() {
  const { chainId } = useSDK();

  const [countdown, setCountdown] = useState(REFETCH_INTERVAL);
  const [currentChainId, setCurrentChainId] = useState(chainId);

  useEffect(() => {
    if (chainId !== currentChainId) {
      setCurrentChainId(chainId);
      mutate("gas-data");
      if (countdown < NETWORK_LATENCY) {
        setCountdown(REFETCH_INTERVAL + NETWORK_LATENCY);
      }
      setCountdown(REFETCH_INTERVAL);
    }
  }, [countdown, chainId, currentChainId, mutate]);

  useEffect(() => {
    const interval = setInterval(() => {
      setCountdown((oldCountdown) => {
        if (oldCountdown <= 0) {
          mutate("gas-data");
          return REFETCH_INTERVAL;
        } else {
          return oldCountdown - STEP_INTERVAL;
        }
      });
    }, STEP_INTERVAL);

    return () => clearInterval(interval);
  }, [chainId]);

  const fetcher = () =>
    fetch(`/api/gas?&chainId=${parseInt(chainId || "0xe704")}`).then((res) =>
      res.json()
    );

  const {
    data: gasData,
    error: gasDataError,
    isLoading: gasDataLoading,
  } = useSWR("gas-data", fetcher);

  if (gasDataLoading) {
    return <p>loading</p>;
  }

  if (gasDataError) {
    return <p className="text-red-500">error</p>;
  }

  const trendingUp = gasData?.baseFeeTrend === "up";
  const trendingDown = gasData?.baseFeeTrend === "down";

  return (
    <HoverCard>
      <HoverCardTrigger asChild>
        <div className="cursor-pointer">
          <div className="flex space-x-1">
            <div className="w-6 h-6">
              <CircularProgressbarWithChildren
                strokeWidth={12}
                styles={buildStyles({
                  pathColor: trendingUp ? "red" : "green",
                })}
                value={
                  ((REFETCH_INTERVAL - countdown) / REFETCH_INTERVAL) * 100
                }
              >
                <>
                  {trendingUp && (
                    <ArrowUp
                      className="animate-bounceUp"
                      color="red"
                      size={14}
                    />
                  )}
                  {trendingDown && (
                    <ArrowDown
                      className="animate-bounceDown"
                      color="green "
                      size={14}
                    />
                  )}
                </>
              </CircularProgressbarWithChildren>
            </div>
            <p>
              ~{formatGweiValues(gasData?.estimatedBaseFee)}
              Gwei
            </p>
            <InfoIcon size={14} />
          </div>
        </div>
      </HoverCardTrigger>
      <HoverCardContent className="p-0 w-80">
        <div className="p-2 space-y-4">
          <p>
            This is an <strong>estimated base gas fee.</strong>
          </p>
          <p>
            50% of the historical base fees are less then or equal to{" "}
            <strong>
              ~{formatGweiValues(gasData?.baseFeePercentile)} Gwei
            </strong>
          </p>
        </div>
      </HoverCardContent>
    </HoverCard>
  );
}
Kopyala

Burada neler yaptığımıza göz atalım:

  1. Sabitler:- REFETCH_INTERVAL, STEP_INTERVAL, NETWORK_LATENCY: Veri çekme aralığını, geri sayım güncellemeleri için adım aralığını ve tahmini ağ gecikmesini yönetmeye yönelik sabitler.

  2. Yardımcı Fonksiyon:- formatGweiValues: Gas fiyatı değerlerini okunabilir bir biçime dönüştüren fonksiyon; küçük değerler için üstel gösterim, büyük değerler için ise sabit noktalı gösterim kullanır.

  3. GasFeeCard Bileşeni:- Mevcut Ethereum chainId'sini almak için useSDK kullanır.

  • countdown ve currentChainId durum değişkenleri başlatılır.

  • İlk useEffect kancası, zincir kimliği değiştiğinde mevcut zincir kimliğini günceller, veri yeniden çekmeyi (mutate) tetikler ve geri sayım zamanlayıcısını sıfırlar.

  • İkinci useEffect, geri sayımı azaltan ve geri sayım sıfıra ulaştığında veri yeniden çekmeyi tetikleyen bir aralık kurar. Bileşen kaldırıldığında aralığı temizler.

  • fetcher: Mevcut zincir kimliğini sorgu parametresi olarak ileterek sunucu taraflı bir API uç noktasından gas verisi çeken fonksiyon.

  • useSWR: fetcher fonksiyonunu kullanarak gas verisi çeken ve veri, yükleme ile hata durumlarını sağlayan kanca.

  1. Render Mantığı:- Bileşen, yükleme ve hata durumlarını koşullu olarak render eder.

  • trendingUp ve trendingDown, çekilen gas verisindeki baseFeeTrend değerine göre belirlenen boolean değişkenlerdir.

  • Ana kullanıcı arayüzü; geri sayım ilerleme durumunu gösteren CircularProgressbarWithChildren ve gas fiyatı eğilimini gösteren bir ok simgesiyle birlikte HoverCard'dan oluşur. Tahmini temel ücreti Gwei cinsinden görüntüler.

  • HoverCardContent, tahmini temel ücret ve geçmiş temel ücret yüzdelik dilimi hakkında daha ayrıntılı bilgi gösterir.

  1. Stil:- Kod, stil için Tailwind CSS sınıflarını kullanır (örneğin text-red-500, cursor-pointer).

  • CircularProgressbarWithChildren, gas fiyatı eğilimine göre renk değiştirecek şekilde stillendirilmiştir (artış için kırmızı, azalış için yeşil).

Ayrıca lucide-react kütüphanesinden ArrowUp, ArrowDown ve InfoIcon simgelerini kullandık. Geriye kalan tek şey, bu bileşeni GetTicket.tsx bileşenine aktarmak; böylece toplam bilet fiyatlarının altında görüntülenecektir.

MetaMask Gas API ile Geliştirmeye Devam Edin

Gas fiyatının farkında olmak, alıcıların satın alma işlemleri için en uygun maliyetli zamanı seçmelerine olanak tanır; düşük gas fiyatı dönemlerinde işlem yaparak tasarruf etmelerini sağlar. Bir etkinlik biletleme platformu için bu bilgiyi sunmak, şeffaflığı ve müşteri memnuniyetini artırır; kullanıcılar harcamaları üzerinde daha fazla kontrol sahibi olduklarını hisseder ve beklenmedik ücretlerden kaçınabilirler.

Gas API'sini mevcut bir etkinlik biletleme dapp'ine entegre ettik. Bu eğitime ait kodu buradan bulabilirsiniz.

Ayrıca aşağıdaki kaynakları incelemenizi öneririm:

Gas API Belgeleri: https://docs.infura.io/infura-expansion-apis/gas-api

Gas API Şablonu: https://github.com/meowyx/GasAPI-Template

Keyifli Geliştirmeler 🚀

Yapay zeka tarafından çevrildi. Hatalar içerebilir. Lütfen her zaman bilgileri doğrulayın.

Çeviriyi değerlendir

Bu makalenin yazarı: