Skip to content

Instantly share code, notes, and snippets.

@kianenigma
Last active January 4, 2022 08:52
Show Gist options
  • Save kianenigma/dac8c7783fb14cd529dc28fe29f4cbe1 to your computer and use it in GitHub Desktop.
Save kianenigma/dac8c7783fb14cd529dc28fe29f4cbe1 to your computer and use it in GitHub Desktop.
import { ApiPromise, WsProvider } from "@polkadot/api";
import { BN } from "@polkadot/util";
import axios from "axios";
async function scrapeSubscan(api: ApiPromise) {
const [from, to] = [3899547, 4876134];
const DOTS = new BN(10000000000);
const HUNDRED = new BN(100);
const REFUND = new BN(new BN(495).mul(DOTS).div(HUNDRED))
const key = process.env['API'] || "DEFAULT_KEY";
console.log(`using api key: ${key}`)
const finalRefunds = [];
function DOT(amount: BN) {
return api.createType('Balance', amount).toHuman()
}
let page = 0;
const rows = 10;
while (true) {
const data = await axios.post(`https://polkadot.api.subscan.io/api/scan/extrinsics`, {
"row": rows,
"page": page,
"module": "electionsphragmen",
"call": "remove_voter",
"signed": "all",
"block_range": `${from}-${to}`,
"no_params": true,
"address": "",
}, { headers: { "X-API-Key": key } })
// @ts-ignore
const txs: any[] = data.data.data.extrinsics;
if (!txs || txs.length === 0) {
break
}
console.log(`fetched remove_voter ${txs.length} transactions from page ${page}`);
for (const tx of txs) {
if (!tx.success) {
continue
}
const who = tx.account_id;
const block = Number(tx.block_num);
const hash = await api.rpc.chain.getBlockHash(block);
const parentHash = (await api.rpc.chain.getHeader(hash)).parentHash;
// @ts-ignore
const postReserved: BN = (await api.query.system.account.at(hash, who)).data.reserved
const parentHashApi = await api.at(parentHash);
// @ts-ignore
const preReserved: BN = (await parentHashApi.query.system.account(who)).data.reserved;
let preDeposit = new BN(0);
if (parentHashApi.query.electionsPhragmen) {
// @ts-ignore
preDeposit = (await parentHashApi.query.electionsPhragmen.voting(who)).deposit
} else if (parentHashApi.query.phragmenElection) {
// @ts-ignore
preDeposit = (await parentHashApi.query.phragmenElection.voting(who)).deposit
} else {
console.log('ERRROR');
throw "could not get PreDeposit from anywhere"
}
if (preDeposit.eq(DOTS.mul(new BN(5)).div(HUNDRED))) {
console.log(`🚨 ${who} at ${block} // preReserve: ${DOT(preReserved)} postReserved: ${DOT(postReserved)} // preDeposit ${DOT(preDeposit)}`)
// this person has left whilst the deposit was recorded 5 milli DOTS.
if (!preReserved.sub(postReserved).abs().eq(preDeposit)) {
// the pre-post reserve's diff must be exactly our expected preDeposit.
console.log('\tbut something seems off here...')
continue
}
if (postReserved.lt(REFUND)) {
console.log(`\t🤷 but this does not have enough reserved balance`);
continue
}
finalRefunds.push(api.createType('AccountId', who).toU8a())
} else {
console.log(`😮‍💨 ${who} at ${block}, not issues with PreDeposit of ${DOT(preDeposit)} [reserved: ${DOT(preReserved)} -> ${DOT(postReserved)}]`)
}
}
page += 1
}
page = 0;
while (true) {
const data = await axios.post(`https://polkadot.api.subscan.io/api/scan/extrinsics`, {
"row": rows,
"page": page,
"module": "electionsphragmen",
"call": "vote",
"signed": "all",
"block_range": `${from}-${to}`,
"no_params": true,
"address": "",
}, { headers: { "X-API-Key": key } })
// @ts-ignore
const txs: any[] = data.data.data.extrinsics;
if (!txs || txs.length === 0) {
break
}
console.log(`fetched vote ${txs.length} transactions from page ${page}, ${txs.filter((t) => !t.success).length} of which are invalid`);
for (const tx of txs) {
if (!tx.success) {
continue
}
const who = tx.account_id;
const block = Number(tx.block_num);
const hash = await api.rpc.chain.getBlockHash(block);
const parentHash = (await api.rpc.chain.getHeader(hash)).parentHash;
// @ts-ignore
const postReserved: BN = (await api.query.system.account.at(hash, who)).data.reserved
const parentHashApi = await api.at(parentHash);
// @ts-ignore
const preReserved: BN = (await parentHashApi.query.system.account(who)).data.reserved;
let preDeposit = new BN(0);
if (parentHashApi.query.electionsPhragmen) {
// @ts-ignore
preDeposit = (await parentHashApi.query.electionsPhragmen.voting(who)).deposit
} else if (parentHashApi.query.phragmenElection) {
// @ts-ignore
preDeposit = (await parentHashApi.query.phragmenElection.voting(who)).deposit
} else {
console.log('ERRROR');
throw "could not get PreDeposit from anywhere"
}
if (preDeposit.eq(DOTS.mul(new BN(5)).div(HUNDRED))) {
console.log(`🚨 ${who} at ${block} // preReserve: ${DOT(preReserved)} postReserved: ${DOT(postReserved)} // preDeposit ${DOT(preDeposit)}`)
if (postReserved.lt(REFUND)) {
console.log(`\t🤷 but this does not have enough reserved balance`);
continue
}
finalRefunds.push(api.createType('AccountId', who).toU8a())
// this person has left whilst the deposit was recorded 5 milli DOTS.
} else {
console.log(`😮‍💨 ${who} at ${block}, not issues with PreDeposit of ${DOT(preDeposit)} [reserved: ${DOT(preReserved)} -> ${DOT(postReserved)}]`)
}
}
page += 1
}
console.log('✅ Done');
console.log(finalRefunds);
}
async function main() {
const provider = new WsProvider("wss://rpc.polkadot.io");
const api = await ApiPromise.create({ provider });
console.log(`Connected to node polkadot ${(await api.rpc.system.chain()).toHuman()} [ss58: ${api.registry.chainSS58}]`)
await scrapeSubscan(api)
}
main().catch(console.error).finally(() => process.exit());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment