Goerli(テストネット)のガス価格をヒートマップで確認できるサイトを作ってみたので解説【開発】

こんにちは、フルオンチェーンNFTクリエイターのnawooです。

先日、GoerliテストネットでNFTのデプロイをおこなっていた際に、「ガス代が不足している」というエラーが出てしまいました。

確認してみると、ガス価格がなんと100gweiを超えていたのです。

nawoo

以前は0.0000001gweiぐらいと安かったのですが、最近は時間帯によってすごく高くなっているようです。

これがEthereumメインネットであれば、こういう時に筆者は以下の「GasNow」というサイトを確認しています。

出典:goerli-gas-heatmap.vercel.app

GasNowでは、1週間分のガス価格を1時間ごとのヒートマップで確認できるので、「平日の夜は高いな」「土日の昼間は安いぞ」といった情報が、一目で分かるようになっています。

しかし、残念ながらGoerliテストネット版のGasNowのようなwebサイトは、執筆時点では見つけられませんでした。

nawoo

Goerliテストネットでもこういうサイトがあるかと思ったのですが、見つけられなかったため、今回は勉強を兼ねて自分で作ってみることにしました。

こうして完成したサイトが、以下の「Goerli Gas Heatmap」です。

出典:goerli-gas-heatmap.vercel.app
nawoo

できるだけ簡単に作りたかったので、ガス価格のヒートマップを表示するだけのシンプルなサイトにしました。

また、ネットワークはGoerliのみです。1時間ごとのガス価格は、単純に毎時00分の最新ブロックのガス価格を使うことにしました。

過去1週間分のヒートマップが確認できます。セルは価格帯ごとに色分けされていて、ひと目でガス価格の推移が分かるようになっています。 セルにマウスを重ねてしばらく待つとツールチップに詳細情報が表示されます。

nawoo

さて、今回の記事では、このサイトをどのように作ったのか解説していきたいと思います。

でははじめに、この記事の構成について説明します。

STEP
開発計画

まずは、「Goerli Gas Heatmap」を構築するにあたりどういった手順で進めるか、またどのような技術スタックを採用するか等について述べていきます。

STEP
準備

さらに、今回の開発をおこなうに当たり必要となるアカウント作成やAPIキーの取得、設定などをおこなっていきます。

STEP
バックエンド開発

続いてバックエンド開発として、Next.jsでAPIを作り、それをVercel Serverless Functionsで実行し、そしてその後でAPIを定期的に呼び出すためにQStashを使っていきます。

STEP
フロントエンド開発

最後に、STEP3で作成したデータが溜まるまでの時間を使い、データベースから情報を読み込んでヒートマップを表示するサイトを作成します。

本記事が、ガス価格のヒートマップを表示するサイトの構築方法、ならびにweb3サービスの具体的な開発手順などについて理解したいと思われている方にとって、少しでもお役に立てれば幸いです。

※本記事は一般的な情報提供を目的としたものであり、法的または投資上のアドバイスとして解釈されることを意図したものではなく、また解釈されるべきではありません。ゆえに、特定のFT/NFTの購入を推奨するものではございませんので、あくまで勉強の一環としてご活用ください。

イーサリアムnaviの「今まで」と「これから」
目次

開発計画

今回のサイトでは、バックエンドとフロントエンドの両方が必要です。

  • バックエンド
    • 1時間ごとにガス価格を取得してデータベースに保存する機能
  • フロントエンド
    • データベースから過去のガス価格を読み込んでヒートマップを表示するWebサイト
nawoo

さて、これをどうやって実現すればいいか、あれこれ調べてみたところ、以下の参考になりそうな記事を見つけました。

▼「Bitcoin(BTC)のチャートを表示するサイトを作成する」という内容の記事
Periodic Data Updates with Next.js and Vercel Serverless Functions

こちらの記事では、バックエンドで1時間ごとにBTC価格を取得してデータベースに保存する方法が、分かりやすく解説されています。

「BTC価格を取得」の部分を、「Goerliのガス価格を取得」に変更すれば、そのまま使えそうですね。

なお、上記事で採用している技術スタックは、以下の通りです。

nawoo

すべて無料で使えるサービスです。筆者も上記事と全く同じ方法を採用することにしました。

また、ブロックチェーンへのアクセスには、Alchemyのノードとethers.jsを使いました。

nawoo

ちなみに「Upstash Redis」「Upstash QStash」「Vercel Serverless Functions」は未経験なので少し不安もありましたが、なんとか頑張ってトライしてみました(笑)

準備

まずは、AlchemyUpstashVecelでそれぞれのアカウントが必要です(いずれも無料で作成できます)。

AlchemyのAPIキーを取得

Alchemyのダッシュボードを開きます。

AppのVIEW KEYボタンをクリックします。(Appが無ければCREATE APPボタンで作成してください。)

CopyボタンでAPI KEYをコピー・メモしておきます。

Upstash Redisの設定

Upstash Redisのコンソールを開きます。Create databaseボタンをクリックします。

NameとRegionを設定して、Createボタンをクリックします。

Details画面が開くので、REST APIのUPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKENをそれぞれコピー・メモしておきます。

Upstash QStashのキーを取得

Upstash QStashのコンソールを開きます。

Request BuilderのSIGNING KEYSをクリックすると、QSTASH_CURRENT_SIGNING_KEYQSTASH_NEXT_SIGNINIG_KEYが表示されるので、こちらもコピーしてメモしておきます。

これで準備は完了です。いよいよバックエンドの開発に進みます。

バックエンド開発

nawoo

まず、Next.jsでAPIを作り、それをVercel Serverless Functionsで実行します。そしてその後、APIを定期的に呼び出すためにQStashを使います。

Next.jsの設定

Next.jsのAPI機能で、update APIを作成します。

このAPIは、呼び出されると最新のガス価格を取得してデータベースに保存してくれます。

nawoo

ということで、最初にNext.jsのプロジェクトを作成します。 プロジェクト名は「goerli-gas-heatmap」にしました。

npx create-next-app --ts

続いて、必要なパッケージをインストールします。

npm install @upstash/redis @upstash/qstash ethers

/pages/apiディレクトリに、update.tsファイルを作成します。このファイルがupdate APIになります。

update APIを作成

update.tsのコードは、以下の通りです(それぞれの詳細は後述します)。

  • (A) RPC URLを取得します
  • (B) eth_feeHistoryで最新のガス価格を取得します
  • (C) ガス情報(feeInfo)を作成します
  • (D) Redisデータベースにガス情報を保存します
  • (E) ステータスコードとガス情報をresponseに設定します
  • (F) verifySignatureで呼び出し元をチェックします
import type { NextApiRequest, NextApiResponse } from "next";
import { Redis } from "@upstash/redis/with-fetch";
import { ethers } from "ethers";
import { verifySignature } from "@upstash/qstash/nextjs";

const MAX_DATA_COUNT = 24 * 30; // 最大30日分をデータベースに保存します

async function handler(req: NextApiRequest, res: NextApiResponse) {
  // (A) alchemyのRPC URLを取得します
  const alchemyApiKey = process.env.ALCHEMY_API_KEY || "";
  if (alchemyApiKey === "") {
    res.status(500).send("ALCHEMY_API_KEY is empty");
    return;
  }
  const url = `https://eth-goerli.g.alchemy.com/v2/${alchemyApiKey}`;

  // (B) eth_feeHistoryを呼び出します
  const provider = new ethers.providers.JsonRpcProvider(url);
  const feeHistory = await provider.send("eth_feeHistory", [
    1,
    "latest",
    [20, 50, 80],
  ]);

  // (C) ガス情報(feeInfo)を作成します
  const feeInfo = {
    block: Number(feeHistory.oldestBlock),
    time: new Date().setUTCMinutes(0, 0, 0),
    base: Number(feeHistory.baseFeePerGas[0]),
    rewards: feeHistory.reward[0].map((s: any) => Number(s)),
  };

  // (D) Redisにガス情報を保存します
  const redis = Redis.fromEnv();
  const redisKey = `goerli-hourly`;
  const lastFeeInfo = await redis.lindex(redisKey, 0);
  const lastTime = lastFeeInfo == null ? 0 : lastFeeInfo.time;
  if (lastTime !== feeInfo.time) {
    await redis.lpush(redisKey, feeInfo);
    await redis.ltrim(redisKey, 0, MAX_DATA_COUNT - 1);
  }

  // (E) status 200を返します (デバッグ用にfeeInfoも返す)
  res.status(200).json(feeInfo);
}

// (F) API呼び出し元がQStashかどうかをチェックします
export default verifySignature(handler);

export const config = {
  api: {
    bodyParser: false,
  },
};

(A) AlchemyのRPC URLを取得

環境変数からAlchemyのAPIキーを読み込んでURLを作成します。

環境変数で直接URLを指定してもよいのですが、今後、Goerli以外にEthereumメインネットなど他のネットワークにも対応させるときのことを考えて、APIキーからURLを作成するようにしました。

(B) eth_feeHistoryでガス価格を取得

EIP-1559とガス価格について

ロンドンアップデートで導入されたEIP-1559で、ガス価格は基本料金(Base fee)とチップ(Priority fee)の2つに分かれました。

nawoo

下の画像を見ると、基本料金(Base)は3つとも同じ18ですが、チップ(Priority)が多いほどブロックに取り込まれるまでの時間が短くなっていることが分かります。

出典:etherscan.io/gastracker

基本料金は同じブロックなら共通ですが、ブロックの混雑具合によって次のブロックの基本料金が増減する仕組みになっています。

  • ブロックのガス使用率が50%より高い場合
    • 次のブロックの基本料金は上がって高くなる
  • ガス使用率が50%より低い
    • 次のブロックの基本料金は下がって安くなる

Etherscanでブロック情報を確認すると、ガス使用量をチェックできます。

出典:etherscan.io/block/15968360

例えば上写真の場合、ガスの上限(Gas Limit)が30,000,000ありますが、ガス使用量(Gas Used)は10,814,605であり、使用率は36.05%となっています。

よって「50%より低いため、次のブロックの基本料金は下がる」ということが分かります。

eth_feeHistoryについて

ガス価格を取得するために eth_feeHistoryメソッドを使います。

eth_feeHistoryでは、複数ブロックの「BaseFee」と「PriorityFeeの分布」をまとめて取得できます。

nawoo

ethers.jsでは関数として定義されていないようなので、今回はsend関数を使いました。

const feeHistory = await provider.send("eth_feeHistory", [
  3, // BlockCount
  "latest", // NewestBlock
  [20, 50, 80], // RewardPercentiles
]);

同じブロックであれば基本料金(BaseFee)は同じですが、チップ(PriorityFee)はトランザクションごとに違うので、パーセンタイルを指定して分布を取得するようになっています。上の例では、20・50・80パーセンタイルのチップ(PriorityFee)を取得しています。

20パーセンタイルとは、小さい順に並べたときに20%の位置にある数値のことです。

nawoo

BlockCountは、最大1024ブロックまで指定できるようです。また、NewestBlockはブロック番号を指定することもできますし、上のように"latest"を指定すると最新ブロックになります。

戻り値のfeeHistoryは、以下のようになります。

{
  oldestBlock: '0x7955eb',
  reward: [
    [ '0x59682f00', '0x59682f00', '0x59682f00' ],
    [ '0x59682f00', '0x59682f00', '0x9502f900' ],
    [ '0x59682f00', '0xa4443ed4', '0xa4443ed4' ]
  ],
  baseFeePerGas: [ '0x139a6dc6ed', '0x12be725db3', '0x12fc375570', '0x12710abae0' ],
  gasUsedRatio: [ 0.3246606, 0.5514911666666666, 0.3854584 ]
}

見やすいように10進数に変換すると、以下のようになります。

{
  oldestBlock: 7951851,
  reward: [
    [ 1500000000, 1500000000, 1500000000 ],
    [ 1500000000, 1500000000, 2500000000 ],
    [ 1500000000, 2755935956, 2755935956 ]
  ],
  baseFeePerGas: [ 84195264237, 80504577459, 81540896112, 79205939936 ],
  gasUsedRatio: [ 0.3246606, 0.5514911666666666, 0.3854584 ]
}

ご覧の通り、こちらには3ブロック分の情報が含まれています。

  • oldestBlock
    • 一番古いブロック番号
      • 取得した3ブロックは 7951851, 7951852, 7951853 であることがわかります。
  • reward
    • 3ブロックの20・50・80パーセンタイルのチップ(PriorityFee)
  • baseFeePerGas
    • 3ブロック+次のブロックの基本料金(BaseFee)
  • gasUsedRatio
    • 3ブロックの混雑具合(ガス使用量の比率)
nawoo

ちなみにbaseFeePerGasは要素数が4つありますが、これは3ブロックに加えて、次のブロックのBaseFeeが含まれているからです。

<補足>
次のブロックのBaseFeeはどうやって決まるのか?

先ほど「ブロックの混雑具合によって次のブロックの基本料金が増減するしくみになっています。」と書きましたが、より詳しく説明すると、現在のブロックのgasUsedRatioによって、次のブロックのBaseFeeは+12.5%から-12.5%の範囲で増減します。

  • gasUsedRatio=1.0 → 次のブロックのBaseFeeは +12.5%
  • gasUsedRatio=0.5 → 次のブロックのBaseFeeは ±0%
  • gasUsedRatio=0.0 → 次のブロックのBaseFeeは -12.5%

上の例では ブロック番号 7951853 の baseFee=81540896112, gasUsedRatio=0.3854584 なので、 BaseFeeの変化率は -12.5% * (0.5-0.3854584)/0.5 = -2.86354% になります。

つまり、次のブロックのBaseFeeは 81540896112 * (100-2.86354)/100 = 79205939936 と求めることができます。

上記の戻り値のbaseFeePerGasの4番目の値とも一致しています。


ヒートマップの作成に話を戻します。

今回は、1時間ごとに最新ブロックの情報を取得すればよいので、BlockCount1NewestBlock"latest"を指定し、チップの分布は20・50・80パーセンタイルを指定しておきます。

nawoo

20がSlow、50がAverage、80がFastのイメージです。 (Etherscan等で表示されているSlow、Average、Fastのガス価格はもっと複雑な計算をしているようですが、今回は単純にしています。)

const feeHistory = await provider.send("eth_feeHistory", [
  1,
  "latest",
  [20, 50, 80],
]);

基本料金+20パーセンタイルのチップの合計値を代表的なガス価格として、ヒートマップで使用します。

50・80パーセンタイルは、詳細情報(ツールチップ)で表示することにしました。

nawoo

最初は50パーセンタイルの値をヒートマップに使っていたのですが、ちょっと高すぎるように感じたので20パーセンタイルに変更しました。

<補足>
provider.getFeeData()関数によるガス価格の取得

ethers.jsではprovider.getFeeData()という関数があり、こちらでもガス価格を取得することができます。

ブロック番号を指定したり、チップの分布を取得することはできませんが、トランザクションに設定するmaxFeePerGasmaxPriorityFeePerGasを取得するにはこの関数が便利です。

const feeData = await provider.getFeeData();
// {
//   lastBaseFeePerGas: 115210245590,
//   maxFeePerGas: 231920491180,
//   maxPriorityFeePerGas: 1500000000,
//   gasPrice: 115311813421
// }

(C) ガス情報(feeInfo)を作成

次にeth_feeHistoryで取得したガス情報を、データベースに保存するためのfeeInfoというオブジェクトに変換します。

feeInfoの型は、以下のようになっています。

{
  block: number; // ブロック番号
  time: number; // 現在日時の年・月・日・時 までをtimestampにしたもの
  base: number; // baseFee (単位はwei)
  rewards: number[] ; // rewards 20・50・80パーセンタイル (単位はwei)
}

1時間ごとのガス価格を保存するので、timeの分・秒・ミリ秒の値は0にしています。

nawoo

実際に実行された時刻が 9:00:05.345 であっても、9:00:00.000 として保存します。

これによって、仮に9時台に複数回実行された場合でも、データベースには1件だけ保存されるようにします。

(D) Redisデータベースに保存

Redisは、いわゆるNoSQLと言われるタイプのデータベースです。

nawoo

Redisのデータ型にはいろいろありますが、今回はリスト型(Lists)を使いました。

1時間ごとのガス情報(feeInfoオブジェクト)をリストに追加し、新しいデータがリストの左側に来るようにします。

さらに30日分(30*24=720件)より前のデータは削除しておきます。

インストールした@upstash/redisライブラリを使うと、とても簡単にRedisを扱うことができます。

ここで使うのは、データの読み込みlindex、データの追加lpush、データのトリムltrimだけです。Object型をそのままデータとして使うことができます。

// 最新データを読み込む(なければnull)
await redis.lindex(key, 0);

// リストの左にデータを追加
await redis.lpush(key, data);

// トリムする(x件を超えた古いデータを削除)
await redis.ltrim(key, 0, x);

実際のコードを見てみましょう。こちらでは、以下の処理をおこなっています。

  • データベースから最新データ(lastFeeInfo)を読み込む
  • 現在のデータとtimeを比較して、異なる場合はデータベースに書き込み+トリム
const redis = Redis.fromEnv();
const redisKey = `goerli-hourly`;
const lastFeeInfo = await redis.lindex(redisKey, 0);
const lastTime = lastFeeInfo == null ? 0 : lastFeeInfo.time;
if (lastTime !== feeInfo.time) {
  await redis.lpush(redisKey, feeInfo);
  await redis.ltrim(redisKey, 0, MAX_DATA_COUNT - 1);
}

1時間ごとに実行されるので、同じ時間帯のデータが重複することは基本的には無いのですが、9:00に実行されるはずが、8:59に実行されてしまうようなケースを考慮して、timeのチェックをしています。

nawoo

ひとつ沼にハマったのが、参考サイトの通りにするとfetch is not definedエラーが出てしまったことです。

これは、VercelのNode.jsのバージョンが16だったことが原因だったため、以下のようにimportを修正することで対応できました。

import { Redis } from '@upstash/redis';
     ↓
import { Redis } from '@upstash/redis/with-fetch';

(E) ステータスコードとガス情報をresponseに設定

responseにはステータスコードだけを設定しても良いのですが、デバッグのためにガス情報も設定しています。

本当はエラー時の処理を書いた方が望ましいのですが、今回は省略しています。

(F) verifySignatureで呼び出し元をチェック

作成したAPIは、そのままの状態では誰でも実行できてしまうので、QStash以外から呼び出せないようにします。 

nawoo

verifySignatureを使って、QStashからの呼び出しかどうかをチェックします。

逆に、誰でも実行できるようにしたい場合は、以下のようにします。

export default verifySignature(handler);
      ↓
export default handler;

Vercelにデプロイ

ここまでに作成したNext.jsのコードをGitHubにプッシュしておきます。

Vercelのダッシュボードを開き、Continue with GitHubボタンをクリックして、リポジトリを選択します。

続いて、Environment Variablesに以下5つの内容を追加します。

  • ALCHEMY_API_KEY : AlchemyのダッシュボードでコピーしたAPI KEY
  • UPSTASH_REDIS_REST_URL : Upstash Redisのコンソールでコピーした内容
  • UPSTASH_REDIS_REST_TOKEN : 〃
  • QSTASH_CURRENT_SIGNING_KEY : Upstash QStashのコンソールでコピーした内容
  • QSTASH_NEXT_SIGNING_KEY : 〃

それぞれNameとValueを設定してAddボタンをクリックします。

Deployボタンをクリックして、しばらく待つとデプロイされます。

デプロイが完了すると、下の画面が表示されるので、ドメインを確認します。

今回はドメインがgoerli-gas-heatmap.vercel.appなので、作成したAPIのURLは https://goerli-gas-heatmap.vercel.app/api/updateになります。

nawoo

このURLは次で使うので、メモしておきましょう。

QStashでスケジュールを追加

Upstash QStashのコンソールを開きます。

作成したAPIのURLを設定し、TypeとEveryを下の画像のように設定したら、Scheduleボタンをクリックします。

これで、毎時00分にupdate APIが実行されるようになりました。

QStashの無料枠では、1日500回までしか使うことができませんが、この設定なら1日24回だけなので問題ありません。

これでバックエンドは完了です。 あとは何もしなくても自動的に1時間ごとにAPIが実行されて、データが蓄積されていきます。

データが溜まるまでの時間でフロントエンドを作っていきましょう。

フロントエンド開発

フロントエンドでは、データベースから情報を読み込んで、ヒートマップを表示するサイトを作成します。 APIと同じNext.jsのプロジェクトを使います。

nawoo

フロントエンドについては、すべて説明するとかなり長くなってしまうため、ポイントだけに絞って説明させてください。

Heatmapライブラリについて

Next.js(React)で使えるヒートマップ用のライブラリを探してみたところ、以下のようにいくつか見つかりました。

しかし、機能が多すぎたり、細かなカスタマイズができなかったりと、どれもしっくりこなかったので、結局自作することにしました。

nawoo

結論としては、CSSフレックボックス(display:flex)を使って<div>タグを並べるだけで、それっぽくなりました。

データ読み出し用のAPIを作成

データベースから情報を読み込む際も、データ読み出し用のAPIを作っておくと便利です。

update APIと同じように、/page/apiディレクトリにget.tsファイルを作成します。

これがget APIとなり、このAPIはデータベースに保存された全データを読み込んで、JSON形式で返します。

import type { NextApiRequest, NextApiResponse } from "next";
import { Redis } from "@upstash/redis/with-fetch";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const redis = Redis.fromEnv();
  const key = "goerli-hourly";
  const data = await redis.lrange(key, 0, -1);
  res.status(200).json(data);
}

RedisでLists型のデータを読み込む際にはlrangeを使います。

nawoo

0から-1までを指定すると、最初から最後までの全データを読み込みます。

await redis.lrange(key, 0, -1); 

なお、このAPIは誰でも実行できるようにしておくため、verifySignatureは使いません。

メインページを作成

1ページだけのサイトなので、pages/index.tsxに書いています。

▼ソースコード
github.com/NowAndNawoo/goerli-gas-heatmap/blob/v1.0.0/pages/index.tsx

  1. データ読み出し用のget APIを使って、データを読み出します。
  2. ヒートマップ用にデータを加工します。
  3. ヒートマップコンポーネントにデータを渡して表示します。

データ読み出し用APIの利用

APIを使ってデータを読み込む際は、SWRを使うと簡単です。

APIのパス(/api/get)を指定するだけで、get APIを呼び出して、結果をFeeInfo[]型として受け取ることができます。

const { data } = useSWR<FeeInfo[], Error>(`/api/get`);

ヒートマップコンポーネント

ヒートマップコンポーネントは、components/heatmap.tsxにあります。

▼ソースコード
github.com/NowAndNawoo/goerli-gas-heatmap/blob/v1.0.0/components/Heatmap.tsx

こちらには、直近8日分×24時間分の<div>タグを並べており、親<div>タグのスタイルには、display:flexを指定しています。

nawoo

また、レスポンシブ対応で、ブラウザの横幅によってセルサイズが変化するようにしました。

詳細情報(ツールチップ)は、JavaScriptやCSSではなく<div title="〜">に設定しているだけです。

この方法では、ツールチップが表示されるまで少し待たされるのが難点なので、今後のバージョンアップでCSSを使ってもっと素早く表示されるツールチップに改良していきたいと考えています。

また、ヒートマップの凡例の数値は、0.1 gwei、1 gwei、10 gwei、100 gwei、300 gweiにしています(数値は固定)。

nawoo

私の個人的な感覚で決めましたが、例えば、過去1週間分の高値安値から自動的に数値を設定する、といった方法もあると思います。これも今後の課題でしょうか。

Vercelにデプロイ

APIと同じで、GitHubにpushすれば自動的にデプロイされます。

Vercelのドメインが goerli-gas-heatmap.vercel.appなので、サイトのURLはそのまま https://goerli-gas-heatmap.vercel.app/になります。

まとめ

【AD】Nouns DAO JAPAN

Nouns DAO JAPANは世界で一番Nounsを広げるコミュニティを目指します。Discord参加はこちら


さて、以上で「Goerliテストネット版のGasNowのようなwebサイト」の完成です。

nawoo

シンプルですが便利なサイトになったのではないでしょうか? また、今回は趣味の開発なので、時間をかけずに完成させることを優先しました。

あちこち手抜き仕様ですし、テストやエラー処理なども省略していますが、本記事がガス価格のヒートマップを表示するサイトの構築方法、ならびにweb3サービスの具体的な開発手順などについて理解したいと思われている方にとって、少しでもお役に立ったのであれば幸いです。

なお、ソースコードはGitHubで公開していますので、よければご参考ください。

励みになりますので、参考になったという方はぜひTwitterでのシェア・コメントなどしていただけると嬉しいです。

イーサリアムnaviを運営するSTILL合同会社では、以下などに関するお問い合わせを受け付けております。

  • 広告掲載
  • リサーチ代行業務
  • アドバイザー業務
  • その他(ご依頼・ご提案・ご相談など)

まずはお気軽に、ご連絡ください。

励みになるので、よかったらSNSなどでシェアしてください!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

フルオンチェーンNFTクリエイター。Lootを見たことがきっかけでNFTやSolidityにハマりました。 今までに作ったフルオンチェーンNFTは以下をご覧ください。

目次