Skip to content

Commit 8abb4b4

Browse files
authored
add payout script / example (#604)
1 parent 8462562 commit 8abb4b4

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

avail-js/examples/node-examples/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"data-submission": "npm run build && node build/node-examples/src/tx-data-availability-data-submission.js",
1212
"create-app-id": "npm run build && node build/node-examples/src/tx-data-availability-create-application-key.js",
1313
"staking-nominate": "npm run build && node build/node-examples/src/tx-staking-nominate-full-example.js",
14+
"staking-payout": "npm run build && node build/node-examples/src/tx-staking-payout-stakers.js",
1415
"staking-validate": "npm run build && node build/node-examples/src/tx-staking-validate-full-example.js",
1516
"estimate-transaction-fees": "npm run build && node build/node-examples/src/estimate-transaction-fees.js",
1617
"subscribe-blocks": "npm run build && node build/node-examples/src/subscribe-blocks.js",
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { ISubmittableResult } from "@polkadot/types/types"
2+
import { H256 } from "@polkadot/types/interfaces/runtime"
3+
import { getKeyringFromSeed, initialize, disconnect } from "avail-js-sdk"
4+
import config from "../../config"
5+
6+
/**
7+
* Script to automate payouts.
8+
*/
9+
const main = async () => {
10+
const api = await initialize(config.endpoint)
11+
12+
/* THINGS YOU CAN CHANGE */
13+
// The account you'll do the payout with
14+
const account = getKeyringFromSeed(config.seed)
15+
const options = { app_id: 0, nonce: -1 }
16+
17+
// Put the list of validators you want to do the payout for, leave empty for all of them
18+
const validatorStashes: string[] = []
19+
20+
// Number of days to check (how many era we'll check for pending rewards)
21+
let nbEra = 1
22+
23+
24+
/* THINGS YOU SHOULD NOT CHANGE */
25+
// Get the active era
26+
const activeEra = (await api.query.staking.currentEra()).toJSON() as number
27+
28+
// Set the starting era to scrap reward
29+
if (nbEra > 84) nbEra = 84
30+
const startEra = activeEra > nbEra ? activeEra - nbEra : 0
31+
32+
// We set a list of eras and validators to claim for
33+
let toClaim: { era: number, validator: string }[] = []
34+
35+
for (let i = startEra; i < activeEra; i++) {
36+
// We get the validators who earned reward during this era
37+
const eraRewardPoints = (await api.query.staking.erasRewardPoints(i)).toJSON() as { total: number, individual: { [address: string]: number } }
38+
const eraRewardPointsValidatorList = Object.keys(eraRewardPoints.individual)
39+
40+
// We get the validators where the payout has already been done for this era
41+
const claimedRewards = (await api.query.staking.claimedRewards.entries(i)).map(x => (x[0].toHuman() as string[])[1])
42+
43+
// We get all validator WITH eraRewardPoints and WITHOUT already claimed reward
44+
let validatorsWithPendingClaim = eraRewardPointsValidatorList.filter(x => !claimedRewards.includes(x))
45+
46+
// We filter by the specified stashes if there are any
47+
if (validatorStashes.length > 0) {
48+
validatorsWithPendingClaim = validatorsWithPendingClaim.filter(x => validatorStashes.includes(x))
49+
}
50+
51+
// We update the global list
52+
toClaim = [...toClaim, ...validatorsWithPendingClaim.map(x => { return { era: i, validator: x } })]
53+
console.log(`Found ${validatorsWithPendingClaim.length} validators with pending claims for era ${i}`)
54+
}
55+
56+
// We create all the transactions
57+
const transactions = await Promise.all(toClaim.map(x => api.tx.staking.payoutStakers(x.validator, x.era)))
58+
const chunks = []
59+
const chunkSize = 5
60+
for (let i = 0; i < transactions.length; i += chunkSize) {
61+
const chunk = transactions.slice(i, i + chunkSize);
62+
chunks.push(chunk);
63+
}
64+
65+
66+
// We batch them together
67+
const batches = chunks.map(x => api.tx.utility.batchAll(x))
68+
for (const [i, tx] of batches.entries()) {
69+
console.log(`Sending batch transaction ${i + 1} of ${batches.length}`)
70+
71+
// Send the batch
72+
const txResult = await new Promise<ISubmittableResult>((res) => {
73+
tx.signAndSend(account, options, (result: ISubmittableResult) => {
74+
if (result.isInBlock || result.isError) {
75+
res(result)
76+
}
77+
})
78+
})
79+
80+
// Error handling
81+
if (!txResult.isError) {
82+
console.log(`Payout done successfully for batch transaction ${i + 1} of ${batches.length} `)
83+
console.log(`Tx Hash: ${txResult.txHash as H256}, Block Hash: ${txResult.status.asInBlock as H256}`)
84+
} else {
85+
console.log(`Transaction was not executed for batch transaction ${i + 1} of ${batches.length}`)
86+
}
87+
}
88+
89+
console.log("Everything was done, bye !")
90+
await disconnect()
91+
process.exit(0)
92+
}
93+
main()

0 commit comments

Comments
 (0)