Last active
October 28, 2022 21:57
-
-
Save nathankellenicki/72ba541dea5c8c0994e23ef52f9512ca to your computer and use it in GitHub Desktop.
Code for finding unique owners of Hot Wheels NFT Garage series's
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import axios from "axios"; | |
import moment from "moment"; | |
const Series = { | |
"Series 1 Employee Rodger Dodger": ["401460"], | |
"Series 1": ["353670", "353671", "353672", "353673", "353674", "353675", "353676", "353677", "353678", "353679", "353680", "353681", "353682", "353683", "362065", "353685", "353686", "353687", "353688", "353689", "353690", "353691", "362066", "353693", "353694", "353695", "353696", "353697", "353698", "362067", "353700", "353701", "353702", "353703", "353704", "362068", "353706", "353707", "353708", "353709"], | |
"Series 2": ["476736", "473693", "473694", "473695", "473696", "473697", "473698", "473699", "473700", "476737", "473702", "473703", "473704", "473705", "473706", "473707", "473708", "473709", "473710", "476738", "473712", "473713", "473714", "473715", "473716", "473717", "473718", "473719", "473720", "476739", "473722", "473723", "473724", "473725", "473726", "473727", "473728", "473729", "473730", "476740", "473732", "473733", "473734"], | |
"Series 3": ["542222", "542223", "542224", "542225", "542226", "542227", "542228", "542229", "542230", "542231", "542232", "542233", "542234", "542235", "542236", "542237", "542238", "542239", "542240", "542241", "542242", "542243", "542244", "542245", "542246", "542247", "542248", "542249", "542250", "542251", "542252", "542253", "542254", "542255", "542256", "542257", "542258", "542259", "542260", "542261", "542262", "542263", "542264", "542265", "542266", "542267", "542268", "542269", "542270", "542271", "542272", "543001", "542475", "542476"], | |
"Ultra Hots": ["603912", "603913", "603914", "603915", "603916", "603917"] | |
}; | |
const Packs = { | |
"Series 1 4 Pack": "353732", | |
"Series 1 10 Pack": "353733", | |
"Series 2 Pack": "477399", | |
"Series 2 Promo Pack": "473735", | |
"Series 3 Pack": "542277", | |
"Series 3 Pack (Re-Mint)": "545272", | |
"Series 3 Promo Pack": "542472", | |
"Ultra Hots Pack": "603901" | |
}; | |
const PackSeriesMapping = { | |
[Packs["Series 1 4 Pack"]]: ["353670", "353671", "353672", "353673", "353674", "353675", "353676", "353677", "353678", "353679", "353680", "353681", "353682", "353683", "362065", "353685", "353686", "353687", "353688", "353689", "353690", "353691", "362066", "353693", "353694", "353695", "353696", "353697", "353698", "362067", "353700", "353701", "353702", "353703", "353704", "362068", "353706", "353707", "353708", "353709"], | |
[Packs["Series 1 10 Pack"]]: ["353670", "353671", "353672", "353673", "353674", "353675", "353676", "353677", "353678", "353679", "353680", "353681", "353682", "353683", "362065", "353685", "353686", "353687", "353688", "353689", "353690", "353691", "362066", "353693", "353694", "353695", "353696", "353697", "353698", "362067", "353700", "353701", "353702", "353703", "353704", "362068", "353706", "353707", "353708", "353709"], | |
[Packs["Series 2 Pack"]]: ["476736", "473693", "473694", "473695", "473696", "473697", "473698", "473699", "473700", "476737", "473702", "473703", "473704", "473705", "473706", "473707", "473708", "473709", "473710", "476738", "473712", "473713", "473714", "473715", "473716", "473717", "473718", "473719", "473720", "476739", "473722", "473723", "473724", "473725", "473726", "473727", "473728", "473729", "473730", "476740", "473732"], | |
[Packs["Series 2 Promo Pack"]]: ["473733", "473734"], | |
[Packs["Series 3 Pack"]]: ["542222", "542223", "542224", "542225", "542226", "542227", "542228", "542229", "542230", "542231", "542232", "542233", "542234", "542235", "542236", "542237", "542238", "542239", "542240", "542241", "542242", "542243", "542244", "542245", "542246", "542247", "542248", "542249", "542250", "542251", "542252", "542253", "542254", "542255", "542256", "542257", "542258", "542259", "542260", "542261", "542262", "542263", "542264", "542265", "542266", "542267", "542268", "542269", "542270", "542271", "542272", "543001"], | |
[Packs["Series 3 Pack (Re-Mint)"]]: ["542222", "542223", "542224", "542225", "542226", "542227", "542228", "542229", "542230", "542231", "542232", "542233", "542234", "542235", "542236", "542237", "542238", "542239", "542240", "542241", "542242", "542243", "542244", "542245", "542246", "542247", "542248", "542249", "542250", "542251", "542252", "542253", "542254", "542255", "542256", "542257", "542258", "542259", "542260", "542261", "542262", "542263", "542264", "542265", "542266", "542267", "542268", "542269", "542270", "542271", "542272", "543001"], | |
[Packs["Series 3 Promo Pack"]]: ["542475", "542476"], | |
[Packs["Ultra Hots Pack"]]: ["603912", "603913", "603914", "603915", "603916", "603917"] | |
}; | |
const lastPulls = {}; | |
const packCounts = {}; | |
const rawData = {}; | |
const getPackStats = async () => { | |
let pageNum = 1; | |
let lastPull = new Date(); | |
const stats7Days = {}; | |
while (lastPull > (new Date()).setDate((new Date()).getDate() - 22)) { | |
const { data } = await axios(`https://wax.api.atomicassets.io/atomicassets/v1/transfers/?template_id=${Object.keys(Packs).map((name) => Packs[name]).join(",")}&recipient=unbox.nft&order=desc&page=${pageNum++}`); | |
for (const transfer of data.data) { | |
if (lastPull < (new Date()).setDate((new Date()).getDate() - 22)) { | |
break; | |
} | |
const { template_id: packTemplateId } = transfer.assets[0].template; | |
stats7Days[packTemplateId] = stats7Days[packTemplateId] || 0; | |
stats7Days[packTemplateId]++; | |
lastPull = new Date(parseInt(transfer.created_at_time, 10)); | |
for (const templateId of PackSeriesMapping[packTemplateId]) { | |
if (lastPulls[templateId] && lastPull > lastPulls[templateId]) { | |
packCounts[templateId] = packCounts[templateId] || 0; | |
packCounts[templateId]++; | |
} | |
} | |
} | |
await new Promise((resolve) => setTimeout(resolve, 300)); // NK: Artificial delay to avoid rate limiting | |
} | |
} | |
const getAccountsWithTemplate = async (seriesId, templateId) => { | |
const { data } = await axios(`https://wax.api.atomicassets.io/atomicassets/v1/transfers/?template_id=${templateId}&sender=premint.nft&order=desc`); | |
const lastPull = new Date(parseInt(data.data[0].assets[0].transferred_at_time, 10)); | |
const { cardid, name, rarity, collection } = data.data[0].assets[0].data; | |
const { max_supply, issued_supply } = data.data[0].assets[0].template; | |
await new Promise((resolve) => setTimeout(resolve, 300)); // NK: Artificial delay to avoid rate limiting | |
const accounts = {}; | |
let total = 0; | |
let pageNum = 1; | |
let hasMore = true; | |
while (hasMore) { | |
const { data } = await axios(`https://wax.api.atomicassets.io/atomicassets/v1/accounts?template_id=${templateId}&page=${pageNum++}`); | |
if (data.data.length <= 0) { | |
hasMore = false; | |
break; | |
} | |
for (const item of data.data) { | |
if (item.account === "premint.nft") { // NK: Exclude premint.nft from owners list | |
continue; | |
} | |
accounts[item.account] = accounts[item.account] || 0; | |
accounts[item.account] += parseInt(item.assets, 10); | |
total += parseInt(item.assets, 10); | |
} | |
await new Promise((resolve) => setTimeout(resolve, 300)); // NK: Artificial delay to avoid rate limiting | |
} | |
lastPulls[templateId] = lastPull; | |
const percent = ((total / issued_supply) * 100).toFixed(2); | |
rawData[seriesId].push([cardid,name,collection,rarity,templateId,Object.keys(accounts).length,total,issued_supply,percent,moment(lastPull).format("MM/DD/YYYY HH:mm:ss")]); | |
return accounts; | |
} | |
const getAccountsWithSeries = async (seriesId) => { | |
rawData[seriesId] = []; | |
const templateCounts = {}; | |
for (const templateId of Series[seriesId]) { | |
const templateAccounts = await getAccountsWithTemplate(seriesId, templateId); | |
for (const account in templateAccounts) { | |
templateCounts[account] = templateCounts[account] || []; | |
templateCounts[account].push(templateAccounts[account]); | |
} | |
} | |
const owners = Object.keys(templateCounts).filter((account) => templateCounts[account].length >= Series[seriesId].length); | |
const counts = {}; | |
for (const account of owners) { | |
counts[account] = Math.min(...templateCounts[account]); | |
} | |
return counts; | |
} | |
const getTotalSetsFromAccounts = (accounts) => { | |
return Object.keys(accounts).reduce((prev, key) => prev + accounts[key], 0); | |
} | |
const getMasterSets = (accountsArray) => { | |
const accounts = []; | |
accountsArray.sort((a, b) => { | |
if (a.length < b.length) { | |
return 1; | |
} | |
if (a.length > b.length) { | |
return -1; | |
} | |
return 0; | |
}); | |
for (const account of Object.keys(accountsArray[0])) { | |
let exists = true; | |
for (let i = 1; i < accountsArray.length; i++) { | |
if (!accountsArray[i][account]) { | |
exists = false; | |
break; | |
} | |
} | |
if (exists) { | |
accounts.push(account); | |
} | |
} | |
return accounts; | |
} | |
(async () => { | |
const querySeries = ["Series 1", "Series 2", "Series 3", "Ultra Hots"]; | |
const seriesResults = []; | |
for (const series of querySeries) { | |
const accounts = await getAccountsWithSeries(series); | |
seriesResults.push(accounts); | |
} | |
await getPackStats(); | |
for (const seriesId in rawData) { | |
console.log("========"); | |
console.log(seriesId); | |
console.log("========"); | |
console.log("CardId,Name,Collection,Rarity,TemplateId,UserCount,TotalCount,IssuedCount,PulledPercentage,LastPull,PacksSinceLastPull"); | |
for (const item of rawData[seriesId]) { | |
if (packCounts[item[4]]) { | |
item.push(packCounts[item[4]]); | |
} else { | |
item.push(0); | |
} | |
console.log(`${item[0]},${item[1]},${item[2]},${item[3]},${item[4]},${item[5]},${item[6]},${item[7]},${item[8]}%,${item[9]},${item[10]}`); | |
} | |
} | |
console.log("========"); | |
console.log("Series,UserCount,SetsCount"); | |
for (const series in querySeries) { | |
const accounts = seriesResults[series]; | |
const sets = getTotalSetsFromAccounts(accounts); | |
console.log(`${querySeries[series]},${Object.keys(accounts).length},${sets}`); | |
} | |
console.log("========"); | |
console.log("Series,UserCount") | |
console.log(`Master Set,${getMasterSets(seriesResults.slice(0, 3)).length}`); | |
console.log(`Master Set (with Ultra hots),${getMasterSets(seriesResults).length}`); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment