今回は、CloudflareWorkerを使って無料でDynamicNFTを作っていきます。
1時間に1回、日本の天気に応じて画像が変わり、気温に応じてプロパティが変わります。
https://testnets.opensea.io/ja/assets/sepolia/0x5cecc8fa3a133d904fdd6db61b47a0d172d67c82/1
はじめに
作り方を説明する前に、DynamicNFTとは?とかなぜCloudflareWorker使うの?って話を少し。
CloudflareWorkerは、個人的に好きだからってのが実は1番の理由だったり。
DynamicNFT?
DynamicNFTの定義があまりよく分かってないのですが、おそらく外部データに基づきメタデータや画像が変更されるNFTのことを指しているのだと思います。
ここでは、1時間ごとのリアルタイムな天気予報?天気情報?(気温、天気)に基づき、メタデータと画像が変化するものを作っていきます。
なぜCloudflareWorker?
Web3ぽくChainlinkを使ったり、もしくはGCPやAWSなど何でも良いのですが、今回は極力無料に抑えるためにCloudflareWorkerを採用しました。
CloudflareWorkerは、無料プランであってもRequests100,000 / dayまで処理することができるし、クレジットカードの登録なしに使えるので、十分かと思います。
https://developers.cloudflare.com/workers/platform/pricing
最低限のWAFは導入されていますが、今回の実装は、少しunsecureです。
全てのオリジンからのリクエストを許可していますので、プロダクトの規模などで必要に応じて対策を講じてくださいね。
どんなdynamicNFT?
1時間に1回外部の天気予報を取得して、Cloudflare WorkerKVというkey-valueストレージにデータ(気温と天気)を保存します。
次に、Cloudflare WorkerKVのデータを元に、NFT用のmetadataを返却します。
サクッと解説
コードはこちら
https://github.com/eggdragons/cloudflare-workers-dynamicNFT
まずは、index.ts
を見てください。
export default {
async fetch(
request: Request,
env: Env
// ctx: ExecutionContext
): Promise<Response> {
return handleRequest(request, env);
},
async scheduled(
event: Event,
env: Env
// ctx: ExecutionContext
): Promise<void> {
await updateMetadata(env);
console.log("cron processed");
},
};
fetch
では、外部からリクエストがあるとmetadataを返却します。
一方scheduled
では、1時間に1回外部の天気予報を取得して、Cloudflare WorkerKVというkey-valueストレージにデータ(気温と天気)を保存します。
このスケジュールした時間にスクリプトを実行できる機能をCloudflare Workers Cronと言います。
次にサクッとhandler.ts
を覗いてみましょう。
テンプレートのように使いまわしているので不要な部分も多々ありますが、今回重要なのは、この部分。
// 一部省略
const router = Router({ base: "/api" });
router.get("/:tokenId", async ({ tokenId }) => {
return await getMetadata(tokenId, env.dynamic, env.IMAGE_PATH);
})
次のURLにアクセスすることで、メタデータが返却されるようになっています。
https://〇〇/api/:tokenId
tokenIdがパスパラメータになっているので、tokenId毎に異なるメタデータが返却されます。
返却されるmetadataはどうなっている?
metadataは次のロジックで返却されています。
- Cloudflare WorkerKVから
temperature
とweather
を取得します。 weather
の値に応じてprefix
の値を決定。- メタデータを作成
image
:imagePath(arweaveの) + prefix + tokenId + ".png"
attributes
にtemperature
とweather
を埋め込み
src/api/getMetadata.ts
を見てみましょう。
const temperature = await kv.get("temperature", { cacheTtl: 3600 });
const weather = await kv.get("weather", { cacheTtl: 3600 });
ここでは、Cloudflare WorkerKVからデータを取得しています。
const data = {
image: imagePath + prefix + tokenId + ".png",
external_url: "",
description: "This is a trial dynamic NFT using cloudflare workers",
name: `dynamicNFT#${tokenId}`,
attributes: [
{
trait_type: "temperature",
value: temperature,
},
{
trait_type: "weather",
value: weather,
},
],
background_color: "",
animation_url: "",
youtube_url: "",
};
ここでは、OpenSeaのMetadata Standardsに乗っ取ってmetadataを作成しています。
定期実行されるCronはどうなっているの?
定期実行されるCronは次のロジックで返却されています。
- weatherapiから天気の情報を取得します。
- Cloudflare WorkerKVへ
temperature
とweather
を保存します。
src/cron/updateMetadata.ts
を見てみましょう。
const result = await fetch(
`http://api.weatherapi.com/v1/current.json?key=${env.WEATHER_API_KEY}&q=tokyo`
)
.then((res) => res.text())
.then((data) => {
return JSON.parse(data);
})
.catch((error) => {
console.error(error);
});
ここでは、weatherapiを用いて、天気情報を取得しています。
await kv.put("temperature", result["current"]["temp_c"]);
await kv.put("weather", result["current"]["condition"]["text"]);
ここでは、取得したデータをCloudflare WorkerKVに保存しています。
作り方
まずは、下記のリポジトリをクローンしてください。
https://github.com/eggdragons/cloudflare-workers-dynamicNFT
クローンし終わったら、必要なパッケージをインストールしていきます。
npm install
cloudflareを導入する
まずは、公式ページでアカウントを作成します。
https://www.cloudflare.com/ja-jp/
次に、Wranglerをインストールしていきます。ドキュメントはこちら。
npm install -g wrangler
その次にターミナルからログインします。
npx wrangler login
workerKVを使う準備をしていきます。
npx wrangler kv:namespace create "dynamic"
すると、
{ binding = "dynamic", id = "*****" }
と帰ってくるので、これをwrangler.toml
ファイルに次のように記載します。
kv_namespaces = [{ binding = "dynamic", id = "*****" }]
テスト用のworkerKVを作る場合は、
npx wrangler kv:namespace create "dynamic" --preview
returnにprewiew_idが書いてあるので、wrangler.toml
ファイルに次のように更新します。
kv_namespaces = [{ binding = "dynamic", id = "*****", preview_id = "*****" }]
シークレットでない環境変数の設定
ついでに、wrangler.toml
ファイルを見ると
ALLOW_ORIGIN = "*"
IMAGE_PATH = "https://arweave.net/D9Ac64K5hIaSqRvni5nSZjDAyqoetrAcDLGiOzLkNcc/"
と記載があります。ここでは、シークレットでない環境変数を保存しています。
IMAGE_PATHには、画像を保存しているフォルダのpathを記載しています。
weatherapi
次に天気予報の情報を取得していきましょう。今回は、weatherapiを使用します。
シークレット環境変数の設定
新規アカウントを取得し、API KEYを入手します。入手できたら、コンソールより環境変数を設定していきます。
シークレットの環境変数は、下記のコマンドを使用します
npx wrangler secret put WEATHER_API_KEY
Enter a secret value:というのが表示されたら、先ほど入手したAPIKEYを入力します。
正常に処理が終わると、Success! Uploaded secret WEATHER_API_KEY
と表示されます。
ブラウザでの環境変数の設定
ここで、一度cloudflareのサイトを確認してみましょう。
cloudflareのサイトにいき、workers & Pages > KV をクリックします。
すると、写真のようにKV storageが出来ている事が確認できるかと思います。
次に、workers & Pages > 概要 をクリックしてください。
dynamic > 設定 > 変数 とクリックしてもらうと、次の画面になります。
この画面から環境変数を設定することも可能です。
通常のテスト
次に、テストを行なってみましょう
.dev.vars
ファイルを作成し、テスト用の環境変数を設定していきます。
その作業が終わったら、次のコマンドを実行します。
npx wrangler dev
色々と注意書きが出てくるので、例えば、Add one to your wrangler.toml file:compatibility_date = "2023-08-10"と表示されたら、
wrangler.toml
ファイルに
compatibility_date = "2023-08-10"
を書き足してください。
テスト環境が整ったので、早速、テストしましょう。別のコンソールを立ち上げ
curl "http://localhost:8787/api/1"
を入力すると、
{"image":"https://arweave.net/D9Ac64K5hIaSqRvni5nSZjDAyqoetrAcDLGiOzLkNcc/Rain1.png","external_url":"","description":"This is a trial dynamic NFT using cloudflare workers","name":"dynamicNFT#1","attributes":[{"trait_type":"temperature","value":null},{"trait_type":"weather","value":null}],"background_color":"","animation_url":"","youtube_url":""}
このようにレスポンスが帰ってくるのがわかります。
定期実行するCronの設定
次に、定期実行するCronの設定を行なっていきます。
wrangler.toml
ファイルを開くと、
[triggers]
crons = ["0 */1 * * *"]
と記載があるかと思います。これが定期実行する際のトリガーの設定になっています。
この設定は、ブラウザからやるのがわかりやすいです。
dynamic > トリガー とクリックしてもらい、Cron トリガーからCron トリガー追加を押します。
ここで好きな時間を設定して、Cronに表示される内容をwrangler.toml
ファイルに記載すると完成です!
定期実行するCronのテスト
今度は、定期実行するCronのテストを行なっていきます。
次のコマンドで、テスト環境を立ち上げて
npx wrangler dev --test-scheduled
別のコンソールから次のコマンドを投げます。
curl "http://localhost:8787/__scheduled?cron=*+*+*+*+*"
すると、Ran scheduled event
と帰ってくると思います。(Ran???)
vitestによるテスト
vitestによるテストも記載しています。
index.test.ts
を開いてもらうと内容がわかるかと思います。
次のコマンドでテスト起動となります。
npm run test
※weatherapiの結果をraw testで埋め込んでいるので、適宜内容変更してください。
デプロイ
デプロイしていきましょう
npx wrangler publish
最後にNFTのtokenURIの設定
NFTのtokenURIには、https://dynamic.eggdragondev.workers.dev/api/
を使用します。
完成!
1時間に1回、日本の天気に応じて画像が変わり、気温に応じてプロパティが変わります。
https://testnets.opensea.io/ja/assets/sepolia/0x5cecc8fa3a133d904fdd6db61b47a0d172d67c82/1
いかがだったでしょうか?他にも色々無料でできる方法を模索しましたが、この方法が一番汎用性が高いと思ったのでご紹介しました!