Last active
January 4, 2022 08:52
-
-
Save kianenigma/dac8c7783fb14cd529dc28fe29f4cbe1 to your computer and use it in GitHub Desktop.
Council refund script - https://github.com/paritytech/polkadot/issues/4160
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 { 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