フルオンチェーンNFTの所有証明やチェーン上データをフルオンチェーン上で取得する[Symbol]

何したの?

Symbol好きなら知ってる人も多いけど(そうじゃない人にまで伝わってそうで凄い)ふぇいとんさんがやりよった。ゲーム本体をSymbolのチェーン上にのっけて、そのまま遊べる凄いやつ。
しかもOpenSeaで売ってて、それを可能にしてるNFT-Driveも凄いなと。

んで、このツイート(別のかも)のリプとかでも見たけど、現状このゲームは誰でも遊べる仕様で、「そのNFTを持ってる人しかプレイできない。」という仕組みにも夢を見るわけで。

当然できるよね、ってことでSSS_Extensionを使ってサンプルを作ってみました。
※SSSの関数がチェーン上にのって消せないので、念の為、作者のいなたつさんにも許可を得てます。(快く承諾してくれた、ってか時間があればやってみたかったみたい)

あと、オンチェーンであってもNODEのURLさえ渡してあげればチェーン上のデータは取ってこれるので、そのへんも使いみちがありそうってことでモザイク取得も実装しときました。

使い方

※コレ自体はたいしたものじゃなく、あくまでもこの先面白いことする人のサンプルになればってものですのであしからず。このへんの機能をオンチェーン上に実装すれば所有証明ができるよーといったものです。
※最下部に全文コード載せときました。3KBでモザイク作成費用と合わせても57XYM、モザイク作成費が50XYMなのでオンチェーン手数料は7XYM。やっす。

※以下リンク

これはNFT-Driveのエクスプローラーで、チェーン上のデータを良きにはからって表示してくれるものです。自前で作成するのも可能ですが、便利なのでこれを使います

もちろんCSSをあてることも可能だけど、それは本題じゃないのでやめた

1.モザイク残高の取得

  • NODE欄にノードのURLを入力してください
     例)https://hideyoshi.mydns.jp:3001 ※メインネットでもテストネットでも

  • Address欄にモザイク残高を取得したいアドレスの入力

  • これは自分が管理するモノじゃなくてもいいですが後ほど所有者かどうかの検証をする場合は自分の管理できるものが良い

  • Submitを押す

はい、これだけですがそのアドレスが所有するモザイクの一覧が表示されます。今回は可分性は考慮していません

2.所有モザイクの検証

02928EBD95B345CF5749CBEC8918AE12AAC9B5A398E640CF1CDEA364581C62A0
  • verify apiに

https://toshimune.npkn.net/verify-sss-token

を入力
※もちろんこれらもハードコーディングできますが、検証用バックエンドは開発者が自前で用意するんだよ、という意味とチェーン上にURLとか載せるのはなんか違うかなと思ったので。
サーバー側でやってるのはこれ
くっそ簡単にAPI作成するならこれがオススメ

  • Verifyボタンを押すとSSS_Extensionが動いて署名を求められる

  • 署名する

  • モザイク残高を取得したアドレスの署名者のアドレスが同じだと背景が黒くなる

つまり黒くなれば署名者と入力したアドレスが一致するので、そのモザイクズ内に該当NFTのアドレスがあれば所有者の証明になるよね。と。

さいごに

以上です!ひさびさにDIYして楽しかった。ゲーム作るとなると数ヶ月や数年かかるので大変ですが、こういうのは数時間なので精神衛生上良いですね。

誰かの、なにかの足しになれば幸いですー

追記:そもそもjavascriptでオンチェーン化されてるので、コード抜き出せばどうとでもなる。ゲーム箇所を暗号化すればいいんだけど、復号には秘密鍵が必要で、つまり暗号化する際にはその公開鍵が必要。制作の時点で保有者は分からんため、公開鍵は知り得ない。どうすればいいだろう?と、ここまで。
追記2:いや、そもそもモザイクIDは発行の時点で分かるので、コーディングする段階では分からない。つまり所有証明できない?あー、色々問題ありそうだね。まぁ、ノードURLさえ渡せばチェーンデータの取得が可能というのは変わらない
追加3 : 追加2に関してはモザイクIDは自身のIDを取得すればいいやん。解決。

あ、そういえば実はNFT-Driveを初めて使ったのだけれどとても良かった。何が良かったってのは一度使ってみてもらえれば分かる。未来的というか、対話式で楽しかった。

今回オンチェーン化したコード

サクッと作りたかったので細かいことは気にしない

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>SymbolFullOnchain</title>
</head>
<body>
<div>NODE: <input type="text" id="node" style="width: 300px;"></div>
<div>Address: <input type="text" id="address" style="width: 400px;"><input type="submit" value="submit" style="margin-left: 10px;" onclick="getMosaics()"></div>
<div id="verify">Verify: <input type="text" id="verifierPublicKey" style="width: 400px;" placeholder="verifier public key"><input type="text" id="verifyApi" style="width: 400px; margin-left: 10px;" placeholder="verify api"><input type="submit" value="verify" style="margin-left: 10px;" onclick="verifyAccount()"></div>
<div>Mosaics: <ul id="mosaics"></ul></div>
<script>
const node = document.getElementById("node");
const address = document.getElementById("address");
const mosaics = document.getElementById("mosaics");
const verifierPublicKey = document.getElementById("verifierPublicKey");
const verifyApi = document.getElementById("verifyApi");
const cookies = document.cookie;
const cookiesArray = cookies.split(';');
for(var c of cookiesArray){
    const cArray = c.split('=');
    if( cArray[0].trim(" ") == 'node'){
        node.value = decodeURIComponent(cArray[1]);
    }
    if( cArray[0].trim(" ") == 'address'){
        address.value = decodeURIComponent(cArray[1]);
    }
    if( cArray[0].trim(" ") == 'verifierPublicKey'){
        verifierPublicKey.value = decodeURIComponent(cArray[1]);
    }
    if( cArray[0].trim(" ") == 'verifyApi'){
        verifyApi.value = decodeURIComponent(cArray[1]);
    }
}
async function getMosaics() {
    document.cookie = `address=${encodeURIComponent(address.value)};`;
    document.cookie = `node=${encodeURIComponent(node.value)};`;
    const res = await fetch(node.value + '/accounts/' + address.value);
    res.json().then((data) => {
        data.account.mosaics.forEach(mosaic => {
            addMosaic(mosaic.id, mosaic.amount)
        });
    })
};
function addMosaic(id, amount){
    const mosaicBox = document.createElement('li');
    const mosaicId = document.createElement('span');
    const mosaicAmount = document.createElement('span');
    mosaicId.innerHTML = id + ": ";
    mosaicAmount.innerHTML = amount;
    mosaicBox.appendChild(mosaicId)
    mosaicBox.appendChild(mosaicAmount)
    mosaics.appendChild(mosaicBox)
}
async function verifyAccount(){
    document.cookie = `verifierPublicKey=${encodeURIComponent(verifierPublicKey.value)};`;
    document.cookie = `verifyApi=${encodeURIComponent(verifyApi.value)};`;
    const token = await window.SSS.getActiveAccountToken(verifierPublicKey.value);
    const res = await fetch(`${verifyApi.value}?publicKey=${window.SSS.activePublicKey}&networkType=${window.SSS.activeNetworkType}&authToken=${token}`);
    res.json().then(data=>{
        if(data.token.signerAddress == address.value){
            document.body.style.background = '#000';
            document.body.style.color = '#FFF';
        }
    })
}
</script>
</body>

この記事が気に入ったらサポートをしてみませんか?