-
-
Save CertainLach/0fbe19dc173714595b064ff1289c99d5 to your computer and use it in GitHub Desktop.
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 } from '@polkadot/api'; | |
import { usingApi, privateKey, sendBalances, sleep, onlySign, signSendAndWait } from './lib'; | |
const main = async (wsEndpoint, accountsCount = 8_000, donorSeed = '//Alice', accountPrefix = 'Account') => { | |
await usingApi(wsEndpoint, async (api: ApiPromise) => { | |
const donor = await privateKey(donorSeed); | |
const accounts = []; | |
console.time('accounts'); | |
for(let i = accountsCount; i--;) { | |
accounts.push((await privateKey(`//${accountPrefix}-${i}`))); | |
} | |
console.timeEnd('accounts'); | |
await sendBalances(api, donor, accounts, 2n * 10n ** 17n); | |
}); | |
} | |
main(['ws://parachain-gateway-deu.cluster.local']).then(() => process.exit(0)).catch(e => { | |
console.error(e); | |
process.exit(1); | |
}); |
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, Keyring, WsProvider } from '@polkadot/api'; | |
import { cryptoIsReady, cryptoWaitReady } from '@polkadot/util-crypto'; | |
import {SignerOptions, SubmittableExtrinsic} from '@polkadot/api/types/submittable'; | |
import {ISubmittableResult, IKeyringPair} from '@polkadot/types/types'; | |
import { ApiTypes } from '@polkadot/api/types'; | |
export type TxResult = { | |
status: 'success' | 'fail', | |
reason?: string, | |
result?: ISubmittableResult, | |
} | |
export type Tx = { | |
signer: IKeyringPair, | |
extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>, | |
options?: Partial<SignerOptions> | undefined, | |
} | |
export const getCb = (unsub: () => void, resolve: (value: any) => void): (result: ISubmittableResult) => void => { | |
return (result) => { | |
const {events, status } = result; | |
// If the transaction wasn't included in the block for some reason: | |
if (status.isDropped) { | |
console.log('TX DROPPED'); | |
unsub(); | |
resolve({status: 'fail', reason: 'ExtrinsicDropped', result}); | |
} | |
else if (status.isFinalized || status.isInBlock) { | |
const errors = events.filter(e => e.event.method === 'ExtrinsicFailed'); | |
if (errors.length > 0) { | |
unsub(); | |
resolve({status: 'fail', reason: 'ExtrinsicFailed', result}); | |
} | |
unsub(); | |
resolve({status: 'success', result}); | |
} | |
else if (status.isRetracted) { | |
console.log('TX RECTRACTED'); | |
} | |
else if (status.isInvalid) { | |
console.log('TX INVALID'); | |
} | |
else if (status.isFinalityTimeout) { | |
console.log('TX FINALITY TIMEOUT'); | |
} | |
else if (status.isNone) { | |
console.log('TX IS NONE'); | |
} | |
else if (status.isUsurped) { | |
console.log('TX IS USURPED'); | |
} | |
else if (status.isBroadcast) {} | |
else { | |
console.log('Unknown status', status, result); | |
} | |
}; | |
} | |
export const signSendAndWait = (transaction: Tx): Promise<TxResult> => { | |
// eslint-disable-next-line no-async-promise-executor | |
return new Promise(async (resolve) => { | |
try { | |
const {signer, extrinsic, options} = transaction; | |
const unsub = await extrinsic.signAndSend(signer, options ? options : {}, getCb(() => unsub(), resolve)); | |
} catch (error) { | |
const reason = error instanceof Error ? error.message : `Unknown error: ${error}`; | |
resolve({status: 'fail', reason}); | |
} | |
}); | |
} | |
export const onlySign = async (transaction: Tx): Promise<SubmittableExtrinsic<ApiTypes, ISubmittableResult>> => { | |
return await transaction.extrinsic.signAsync(transaction.signer, transaction.options); | |
} | |
export const usingApi = async (wsEndpoint: string, func: (api: ApiPromise) => Promise<void>, options?: {[key: string]: any}) => { | |
const api = new ApiPromise({provider: new WsProvider(wsEndpoint), ...options}); | |
await api.isReadyOrError; | |
try { | |
await func(api); | |
} | |
catch(e) { | |
console.error(e); | |
} | |
await api.disconnect(); | |
} | |
export const privateKey = async (seed: string) => { | |
if(!cryptoIsReady()) await cryptoWaitReady(); | |
return (new Keyring({type: 'sr25519'})).addFromUri(seed); | |
} | |
export const sleep = (time: number) => { | |
return new Promise((resolve: (value: unknown) => void) => { | |
setTimeout(() => resolve(true), time); | |
}); | |
} | |
export const sendBalances = async (api: ApiPromise, signer: IKeyringPair, accounts: IKeyringPair[], sum = 200n * 10n ** 18n) => { | |
console.time('balances sign'); | |
let signerAccount = (await api.query.system.account(signer.address)).toJSON() as any; | |
console.log(signerAccount); | |
let nonce = signerAccount.nonce; | |
console.log('signer', signer.address); | |
console.log('signer nonce', nonce); | |
const txs = [] as any; | |
for(let acc of accounts) { | |
txs.push({ | |
nonce: nonce, | |
tx: await onlySign({ | |
signer: signer, | |
extrinsic: api.tx.balances.transfer(acc.address, sum), | |
options: {era: 0, nonce: nonce++} | |
}) | |
}); | |
} | |
console.timeEnd('balances sign'); | |
console.time('balances send') | |
let chunk = []; | |
for(let tx of txs) { | |
if(tx.nonce % 100 === 0) console.log('Send ', tx.nonce); | |
chunk.push(async () => await tx.tx.send()); | |
if(chunk.length >= 30) { | |
await Promise.all(chunk.map(x => x())); | |
chunk = []; | |
} | |
} | |
if(chunk.length > 0) await Promise.all(chunk.map(x => x())); | |
console.timeEnd('balances send'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment