見出し画像

特定のNFTコレクションをあるタイミングで保持するウオレットのリストを取得する今一番カンタンな方法(おまけは有料)

私は下記の情報サイトでWalletの情報を収集し自動解析を行っています。

https://oneseep.xyz/statics

NFTのコレクションを数多くさばいても、サーバーの処理が重くならないように効率的にWalettの情報を取得する必要がでてきます。

そのときに使っているやり方を今日は紹介しましょう。無料部分だけで再現できると思います。通しで動くソースコードや、取得後の処理を紹介するところを投げ銭的に有料ラッピングさせてもらいます

ブロックチェーンとして直接的な方法

NFTを所持しているとは、NFTのスマートコントラクトがこの人が持っていますよと証明したときにそのNFTの所有権があなたにあるといえるのです。

物理的なお金と違い、実はお財布の中には何も入っていません。ということで直接的にその所持を証明するためにはスマートコントラクトにコマンドを投げ回答をもらうのが一番直接的で正しいです。

ERC721のコントラクトを作成したときに必ず下記のコマンドが必要となります。これはERC721を使っていると言うための必須条件です

function ownerOf(uint256 _tokenId) external view returns (address);

なので一番正しいのは、TokenIdを最初から順番に引数として与え、スマコンに聞くことなのです。

実際に取得に使ったプログラム例です

const Web3 = require('web3');
const abi = require("./abi.json")  // get from opensea
const fs = require('fs');


const NODE_URL = "https://xxxxxx"; //set your node url
const contractAddress = "0x31dcdd48e0b75009d0657281beeba33cf21ec060"; // change  example

const provider = new Web3.providers.HttpProvider(NODE_URL);
const web3 = new Web3(provider);

const contract = new web3.eth.Contract(abi, contractAddress);

let owners={};
const info = async () => {
    for (let i = 1; i <= 8888; i++) {
        try{
            owner = await contract.methods.ownerOf(i).call();
            //console.log("owner: ", owner);
            if(owner != "0x0000000000000000000000000000000000000000"){
                owners[owner] = owners[owner] + 1 || 1;
            }
        }
        catch(e){
            //console.log("no user " + i);
        }
    }
    console.log(owners);
}
info();

この関数に聞くこと自体にはガス代はかかりませんので無料ではあるのですが時間がかかります。

では早い方法は…

現在一番お手軽かつ早くNFTを保持するWalletの情報を得るのは AlchemyのAPIを使う方法です。AlchemyのAPIをエンドポイントに直接的にWalletのアドレスを配列として返すものがあります。NFTの運営者や私のような解析者向けに作られたものだと思います。

getOwnersForCollection

こちらにドキュメントがありますが、AlchemyにログインしないとアクセスできませんのでAlchemyのアカウントを作りましょう。

https://alchemy.com/?r=6dc77a63df578872

リファラル付きではありますが、現金ではなくAlchemyで使える$100の権利がお互いもらえるようなので、もらえるものは貰っておきましょう。無料プランの間は意味はないですが。

https://eth-mainnet.g.alchemy.com/nft/v2/{apiKey}/getOwnersForCollection

https://eth-mainnet.g.alchemy.com/nft/v2/{apiKey}/getOwnersForCollection

このエンドポイントにコントラクトアドレスを与えれば答えが戻ってきます。今回はEthereumのメインネットですが、その他 Polygon, Arbitrum, Optimismが対応しています。

apiKeyは、Alchemyのアカウントを作成しAPPを作成すれば取得することができます。

これが私のAlchemyにログインしたときのトップページです。 CREATE APPというボタンが右上にありますので、サクッと作ってください。APPはリストで出てきますので右側のVIEW KEYで取得できます。
例えばKEYが    xxxxxxxxxxxxx だったら

https://eth-mainnet.g.alchemy.com/nft/v2/xxxxxxxxxxxxx/getOwnersForCollection

でいいわけですね。Pyyhonだったら

import requests

url = "https://eth-mainnet.g.alchemy.com/nft/v2/xxxxxxxxxxx/getOwnersForCollection?contractAddress=0xe785E82358879F061BC3dcAC6f0444462D4b5330&withTokenBalances=false"

headers = {"accept": "application/json"}
response = requests.get(url, headers=headers)

print(response.text)


コントラクトのアドレスはOpenSeaから辿ることができますので分かっていない場合は調べた上で設定しましょう。

戻り値は下記のようにとてもシンプルなアドレスの配列として戻ってきます。これがとても使いやすい!

{
"ownerAddresses": [
"0x00189b281d7249950ecbe735653536c4a1821351",
"0x0026691eed3d9ca5dcf7351dfbe113e6dfce528d",
"0x002bc879ecf65cf7ec66e02456547120e72aa11a",
"0x002c6f95a7f41f14313f09aa4b77e674c6727d48",
"0x00749e4ddf6b386ee4eb9c7b8540b15602955c60",
"0x0075601db47c244cf3a3cda72f64a0e3fe12dd12",
"0x0078978d493a5a0db7ef37f5cd0b4e89adebfb42",
"0x007abdd190137f614701a6216fd703329afbcdf1",
"0x007c60b97c56952600bf25d65ff79abb801078b0",
"0x0087d8098d22b00692b160899245994d3cc58275",
"0x008e070f53cdd0cdd202a35c93b26f560abbb4c2",
"0x009a57f76a65fef6e81a57f4ad9dd0542fc98f41",
…
]
}


withTokenBalances=false

このオプションをtrueにすると何個持っているのかも返ってきます。必要に応じて活用ください。ただ、下記のように処理が面倒な形になるので必要なければfalseにしておくのが良いです。

{
  "ownerAddresses": [
    {
      "ownerAddress": "0x00189b281d7249950ecbe735653536c4a1821351",
      "tokenBalances": [
        {
          "tokenId": "0x000000000000000000000000000000000000000000000000000000000000011d",
          "balance": 1
        }
      ]
    },
    {
      "ownerAddress": "0x0026691eed3d9ca5dcf7351dfbe113e6dfce528d",
      "tokenBalances": [
    

時間指定をしよう

walletの一覧が取ることができるようになりました。最新のものだけほしければこれでいいのですが、時間指定することにより、時間をさかのぼった解析や、スナップショット時の運営時の安心感が変わってきます。スナップショットを取るタイミングでAPIを叩くというのはトラブルがあったときは時間がずれてしまいます。そんなドキドキはいりません。スマートコントラクトは時系列にすべてのデーターが埋まっています。後にも遡れるのです。

Alchemy のAPIの魅力はブロックタイムを指定できることなのです

block=<ブロック番号>

を指定してやることで特定の時間のWalett所持の情報が取れます。簡単ですね!えっ。ブロック番号がわかりませんか???そうですよね。

この記事執筆時でBLOCK HIGHTは 15810420 です。Etherscanのサイトに行けばすぐにわかります。

https://etherscan.io/blocks

でもスマートではありませんね。次は EtherscanのAPIの力を借りましょう。Etherscanの提供するAPIが時間からブロックタイムを返してくれます。

Etherscanのアカウントを作ってください。そしてAPI KEYのタブをクリックして、My API Keys で +ADDしてキーを作成してください。無料でいけます。API KEYの取得はこのあたりを参照頂いても良いかと(https://qiita.com/taijusanagi/items/c34d14767feeb3e131a8)

で今回使うAPIのエンドポイントはこちら
Get Block Number by Timestamp

https://api.etherscan.io/api
?module=block
&action=getblocknobytime
&timestamp=<unix
 time>
&closest=before
&apikey=<YourApiKeyToken>

この関数はtimestampにunix timeを入れてやるとその時間に近い block 番号を返してきます。

unix timeに不慣れな方はPythonであれば Datetime関数を使うと覚えてください。私の場合は下記のように作成していますので参考にしてください。

year = 2022
month = 10
day = 24
hour = 19
min = 56
dt = datetime.datetime(year, month, day, hour, min)
unix_time = int(dt.timestamp())

dt.datetime()は floatで値を戻してくるので APIに叩き込む前にInt型になおしてやる必要がある。上記時間はPCの場所に依存するのでPCのタイムゾーンに注意して使ってください。

ということでここで見つけてきたブロックタイムをAlchemyのAPIに使ってやれば、好きな時間を指定できます。

Pythonの例

ココまでをまとめると下記のようなコードでいいわけですね

import datetime
import requests

ETHERSCAN_API_KEY = 'YourApiKeyToken'
ALCHEMY_API_KEY = 'YourApiKeyToken'
contract_address = 'contract_address'
year = 2022
month = 10
day = 20
hour = 12
minute = 0
dt = datetime.datetime(year, month, day, hour, minute)
unix_time = int(dt.timestamp())

url_base ='https://api.etherscan.io/api?module=block&action=getblocknobytime&closest=before'
url = f'{url_base}&timestamp={unix_time}&apikey={ETHERSCAN_API_KEY}'
block_number = requests.get(url).json()['result']

url_base = f'https://eth-mainnet.g.alchemy.com/nft/v2/{ALCHEMY_API_KEY}/getOwnersForCollection?'
url = f"{url_base}contractAddress={contract_address}&block={block_number}"
owners = requests.get(url, headers={"accept": "application/json"}).json()['ownerAddresses']
print(owners)

タイトル回収しました
後は応用です。時間をずらしてデータをとって時間変化を見たり、2つのコレクタブルのウオレットリストを取得して、重複ウオレットの数を数えたりなどができます。

Pythonの良い教材は、私が紹介する必要はなく世の中にいっぱいあります。もしもクリプト系の教材で勉強したかったら下記を活用ください。YouTubeの動画は無料です。レベル1からレベル5くらいには進化できます。


ここから下は有料にしました。投げ銭的な場所ですのでよかったらご活用ください。


Q&A形式で記載しています。例えばデーターを取った後、重複するデーターの抽出方法など書いています。購入頂いた方は優先的にお答えします。ので買ったよといいながら質問ください。

質問はこちらに -> https://discord.gg/CrAjnZH9dF

頂いた質問と回答は下記のQ&Aに追記していきますね。

ここから先は

3,094字 / 2画像

¥ 800

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