Skip to content

Commit

Permalink
v2.4.1: story protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
munris-vlad committed Nov 14, 2024
1 parent 22949ab commit 741b1e4
Show file tree
Hide file tree
Showing 16 changed files with 609 additions and 5 deletions.
380 changes: 380 additions & 0 deletions checkers/story.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,380 @@
import '../utils/common.js'
import {
sleep,
readWallets,
getBalance,
getKeyByValue,
getProxy,
newAbortSignal,
ethPrice,
timestampToDate
} from '../utils/common.js'
import axios from "axios"
import { Table } from 'console-table-printer'
import { createObjectCsvWriter } from 'csv-writer'
import moment from 'moment'
import cliProgress from 'cli-progress'
import { config } from '../user_data/config.js'
import { cleanByChecker, getCountByChecker, getWalletFromDB, saveWalletToDB } from '../utils/db.js'
import { formatEther, parseEther } from 'viem'

const columns = [
{ name: 'n', color: 'green', alignment: "right" },
{ name: 'wallet', color: 'green', alignment: "right" },
{ name: 'IP', alignment: 'right', color: 'cyan' },
{ name: 'TX Count', alignment: 'right', color: 'cyan' },
{ name: 'Badge Count', alignment: 'right', color: 'cyan' },
{ name: 'Main', alignment: 'right', color: 'cyan' },
{ name: 'Wand', alignment: 'right', color: 'cyan' },
{ name: 'Nightly', alignment: 'right', color: 'cyan' },
{ name: 'D3X', alignment: 'right', color: 'cyan' },
{ name: 'Satori', alignment: 'right', color: 'cyan' },
{ name: 'MahojinIP', alignment: 'right', color: 'cyan' },
{ name: 'ArtStory', alignment: 'right', color: 'cyan' },
{ name: 'PunkgaMe', alignment: 'right', color: 'cyan' },
{ name: 'StandartProtocol', alignment: 'right', color: 'cyan' },
{ name: 'Rightsfually', alignment: 'right', color: 'cyan' },
{ name: 'Contracts', alignment: 'right', color: 'cyan' },
{ name: 'Days', alignment: 'right', color: 'cyan' },
{ name: 'Weeks', alignment: 'right', color: 'cyan' },
{ name: 'Months', alignment: 'right', color: 'cyan' },
{ name: 'First tx', alignment: 'right', color: 'cyan' },
{ name: 'Last tx', alignment: 'right', color: 'cyan' },
]

const headers = [
{ id: 'n', title: '№' },
{ id: 'wallet', title: 'wallet' },
{ id: 'IP', title: 'IP' },
{ id: 'TX Count', title: 'TX Count' },
{ id: 'Badge Count', title: 'TX Count' },
{ id: 'Main', title: 'Main' },
{ id: 'Wand', title: 'Wand' },
{ id: 'Nightly', title: 'Nightly' },
{ id: 'D3X', title: 'D3X' },
{ id: 'Satori', title: 'Satori' },
{ id: 'MahojinIP', title: 'MahojinIP' },
{ id: 'ArtStory', title: 'ArtStory' },
{ id: 'PunkgaMe', title: 'PunkgaMe' },
{ id: 'StandartProtocol', title: 'StandartProtocol' },
{ id: 'Rightsfually', title: 'Rightsfually' },
{ id: 'Contracts', title: 'Contracts' },
{ id: 'Days', title: 'Days' },
{ id: 'Weeks', title: 'Weeks' },
{ id: 'Months', title: 'Months' },
{ id: 'First tx', title: 'First tx' },
{ id: 'Last tx', title: 'Last tx' },
]

const args = process.argv.slice(2)
if (args[1] === 'refresh') {
cleanByChecker('story')
}

let p
let csvWriter
let stats = []
let wallets = readWallets(config.modules.story.addresses)
let iterations = wallets.length
let iteration = 1
let csvData = []
let jsonData = []
let apiUrl = 'https://odyssey.storyscan.xyz/api/v2/addresses'
const cancelTimeout = 15000
const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic)

const reqHeaders = {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9,ru;q=0.8,bg;q=0.7",
"priority": "u=1, i",
"sec-ch-ua": "\"Chromium\";v=\"130\", \"Google Chrome\";v=\"130\", \"Not?A_Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"Referer": "https://odyssey.storyscan.xyz/address/0x2300f68064BfaafA381cd36f2695CDfEAAc09231?tab=txs",
"Referrer-Policy": "origin-when-cross-origin"
}

async function getBalances(wallet) {
let ipBalanceDone
let ipBalanceRetry = 0

let badgeBalanceDone
let badgeBalanceRetry = 0

while (!ipBalanceDone) {
await axios.get(`${apiUrl}/${wallet}`, {
signal: newAbortSignal(cancelTimeout),
httpsAgent: getProxy(0, true),
}).then(async response => {
stats[wallet].balances['IP'] = formatEther(parseEther(response.data.coin_balance)) / Math.pow(10, 18)
ipBalanceDone = true
}).catch(function (error) {
if (config.debug) console.log(error)
ipBalanceRetry++
if (ipBalanceRetry > 3) {
ipBalanceDone = true
}
})
}

while (!badgeBalanceDone) {
await axios.get(`${apiUrl}/${wallet}/tokens?type=ERC-721`, {
signal: newAbortSignal(cancelTimeout),
httpsAgent: getProxy(0, true),
}).then(async response => {
const tokens = response.data.items
for (const token of tokens) {
stats[wallet].balances[token.token.symbol] = 1
}
badgeBalanceDone = true
}).catch(function (error) {
if (config.debug) console.log(error)

badgeBalanceRetry++
if (badgeBalanceRetry > 3) {
badgeBalanceDone = true
}
})
}
}

async function getTxs(wallet) {
const uniqueDays = new Set()
const uniqueWeeks = new Set()
const uniqueMonths = new Set()
const uniqueContracts = new Set()

let txs = []
let params = {
block_number: '',
index: '',
items_count: ''
}
let isAllTxCollected = false, retry = 0

while (!isAllTxCollected && retry < 3) {
await axios.get(`${apiUrl}/${wallet}/transactions`, {
params: params.block_number === '' ? {} : params,
signal: newAbortSignal(cancelTimeout),
httpsAgent: getProxy(0, true),
headers: reqHeaders
}).then(async response => {
let items = response.data.items

Object.values(items).forEach(tx => {
txs.push(tx)
})

if (response.data.next_page_params === null) {
isAllTxCollected = true
} else {
params = response.data.next_page_params
}
}).catch(function (error) {
if (config.debug) console.log(error)

retry++
})
}

stats[wallet].txcount = txs.length

Object.values(txs).forEach(tx => {
const date = new Date(tx.timestamp)
uniqueDays.add(date.toDateString())
uniqueWeeks.add(date.getFullYear() + '-' + date.getWeek())
uniqueMonths.add(date.getFullYear() + '-' + date.getMonth())

if (tx.from) {
if (tx.from.hash.toLowerCase() === wallet.toLowerCase()) {
if (tx.to) {
uniqueContracts.add(tx.to.hash)
}
}
}
})

const numUniqueDays = uniqueDays.size
const numUniqueWeeks = uniqueWeeks.size
const numUniqueMonths = uniqueMonths.size
const numUniqueContracts = uniqueContracts.size
if (txs.length) {
stats[wallet].first_tx_date = new Date(txs[txs.length - 1].timestamp)
stats[wallet].last_tx_date = new Date(txs[0].timestamp)
stats[wallet].unique_days = numUniqueDays
stats[wallet].unique_weeks = numUniqueWeeks
stats[wallet].unique_months = numUniqueMonths
stats[wallet].unique_contracts = numUniqueContracts
}
}

async function fetchWallet(wallet, index, isFetch = false) {
const existingData = await getWalletFromDB(wallet, 'story')
if (existingData && !isFetch) {
stats[wallet] = JSON.parse(existingData)
} else {
stats[wallet] = {
txcount: 0,
badgecount: 0,
balances: { IP: 0 },
}

await getBalances(wallet)
await getTxs(wallet)
}

let badgeCount = 0

badgeCount += stats[wallet].balances['OTCIPA'] ? 1 : 0
badgeCount += stats[wallet].balances['WAND'] ? 1 : 0
badgeCount += stats[wallet].balances['NIGHTLY'] ? 1 : 0
badgeCount += stats[wallet].balances['D3XBadge'] ? 1 : 0
badgeCount += stats[wallet].balances['SatoriBadge'] ? 1 : 0
badgeCount += stats[wallet].balances['$MahojinIPBadge'] ? 1 : 0
badgeCount += stats[wallet].balances['ARTSTORYBADGE'] ? 1 : 0
badgeCount += stats[wallet].balances['PunkgaMeBadge'] ? 1 : 0
badgeCount += stats[wallet].balances['STND-STORYBADGE'] ? 1 : 0
badgeCount += stats[wallet].balances['rfally'] ? 1 : 0

stats[wallet].badgecount = badgeCount
progressBar.update(iteration)

p.addRow({
n: parseInt(index) + 1,
wallet: wallet,
'IP': parseFloat(stats[wallet].balances['IP']).toFixed(4),
'TX Count': stats[wallet].txcount,
'Badge Count': stats[wallet].badgecount,
'Main': stats[wallet].balances['OTCIPA'] ? 'Yes' : 'No',
'Wand': stats[wallet].balances['WAND'] ? 'Yes' : 'No',
'Nightly': stats[wallet].balances['NIGHTLY'] ? 'Yes' : 'No',
'D3X': stats[wallet].balances['D3XBadge'] ? 'Yes' : 'No',
'Satori': stats[wallet].balances['SatoriBadge'] ? 'Yes' : 'No',
'MahojinIP': stats[wallet].balances['$MahojinIPBadge'] ? 'Yes' : 'No',
'ArtStory': stats[wallet].balances['ARTSTORYBADGE'] ? 'Yes' : 'No',
'PunkgaMe': stats[wallet].balances['PunkgaMeBadge'] ? 'Yes' : 'No',
'StandartProtocol': stats[wallet].balances['STND-STORYBADGE'] ? 'Yes' : 'No',
'Rightsfually': stats[wallet].balances['rfally'] ? 'Yes' : 'No',
'Contracts': stats[wallet].unique_contracts ?? 0,
'Days': stats[wallet].unique_days ?? 0,
'Weeks': stats[wallet].unique_weeks ?? 0,
'Months': stats[wallet].unique_months ?? 0,
'First tx': stats[wallet].txcount ? moment(stats[wallet].first_tx_date).format("DD.MM.YY") : '-',
'Last tx': stats[wallet].txcount ? moment(stats[wallet].last_tx_date).format("DD.MM.YY") : '-',
})

jsonData.push({
n: parseInt(index) + 1,
wallet: wallet,
'IP': parseFloat(stats[wallet].balances['IP']).toFixed(4),
'TX Count': stats[wallet].txcount,
'Badge Count': stats[wallet].badgecount,
'Main': stats[wallet].balances['OTCIPA'] ? 1 : 0,
'Wand': stats[wallet].balances['WAND'] ? 1 : 0,
'Nightly': stats[wallet].balances['NIGHTLY'] ? 1 : 0,
'D3X': stats[wallet].balances['D3XBadge'] ? 1 : 0,
'Satori': stats[wallet].balances['SatoriBadge'] ? 1 : 0,
'MahojinIP': stats[wallet].balances['$MahojinIPBadge'] ? 1 : 0,
'ArtStory': stats[wallet].balances['ARTSTORYBADGE'] ? 1 : 0,
'PunkgaMe': stats[wallet].balances['PunkgaMeBadge'] ? 1 : 0,
'StandartProtocol': stats[wallet].balances['STND-STORYBADGE'] ? 1 : 0,
'Rightsfually': stats[wallet].balances['rfally'] ? 1 : 0,
'Contracts': stats[wallet].unique_contracts ?? 0,
'Days': stats[wallet].unique_days ?? 0,
'Weeks': stats[wallet].unique_weeks ?? 0,
'Months': stats[wallet].unique_months ?? 0,
'First tx': stats[wallet].txcount ? stats[wallet].first_tx_date : '—',
'Last tx': stats[wallet].txcount ? stats[wallet].last_tx_date : '—',
})

if (stats[wallet].txcount > 0) {
await saveWalletToDB(wallet, 'story', JSON.stringify(stats[wallet]))
}

iteration++
}

async function fetchBatch(batch) {
await Promise.all(batch.map((account, index) => fetchWallet(account, getKeyByValue(wallets, account))))
}

async function fetchWallets() {
wallets = readWallets(config.modules.story.addresses)
iterations = wallets.length
csvData = []
jsonData = []
iteration = 1

csvWriter = createObjectCsvWriter({
path: './results/story.csv',
header: headers
})

p = new Table({
columns: columns,
sort: (row1, row2) => +row1.n - +row2.n
})

let batchSize = 10
let timeout = 5000

const walletsInDB = await getCountByChecker('story')

if (walletsInDB === wallets.length) {
batchSize = walletsInDB
timeout = 0
}

const batchCount = Math.ceil(wallets.length / batchSize)
const walletPromises = []

for (let i = 0; i < batchCount; i++) {
const startIndex = i * batchSize
const endIndex = (i + 1) * batchSize
const batch = wallets.slice(startIndex, endIndex)

const promise = new Promise((resolve) => {
setTimeout(() => {
resolve(fetchBatch(batch))
}, i * timeout)
})

walletPromises.push(promise)
}

return Promise.all(walletPromises)
}

async function saveToCsv() {
p.table.rows.map((row) => {
csvData.push(row.text)
})
csvData.sort((a, b) => a.n - b.n)
csvWriter.writeRecords(csvData).then().catch()
}

export async function storyFetchDataAndPrintTable() {
progressBar.start(iterations, 0)
await fetchWallets()
await saveToCsv()
progressBar.stop()
p.printTable()
}

export async function storyData() {
await fetchWallets()
await saveToCsv()

return jsonData
}

export async function storyFetchWallet(wallet) {
return fetchWallet(wallet, getKeyByValue(wallets, wallet), true)
}

export async function storyClean() {
await cleanByChecker('story')
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wallet-checker",
"version": "2.4.0",
"version": "2.4.1",
"scripts": {
"start": "node utils/index.js"
},
Expand Down
Loading

0 comments on commit 741b1e4

Please sign in to comment.