import BN from 'bn.js'; import { ApiPromise } from '@polkadot/api'; import { Index } from '@polkadot/types/interfaces'; import { SubmittableExtrinsic } from '@polkadot/api/types'; import { KeyringPair } from '@polkadot/keyring/types'; export class Sender { private readonly api: ApiPromise; private static nonceMap: Map = new Map(); constructor(api: ApiPromise) { this.api = api; } private async getNonce(address: string): Promise { let oncahinNonce: BN = new BN(0); if (!Sender.nonceMap.get(address)) { oncahinNonce = await this.api.query.system.accountNonce(address); } let nonce: BN | undefined = Sender.nonceMap.get(address); if (!nonce) { nonce = oncahinNonce; } const nextNonce: BN = nonce.addn(1); Sender.nonceMap.set(address, nextNonce); return nonce; } private clearNonce(address: string): void { Sender.nonceMap.delete(address); } public async signAndSend( tx: SubmittableExtrinsic<'promise'>, account: KeyringPair, expectFailure = false ): Promise { return new Promise(async (resolve, reject) => { const nonce: BN = await this.getNonce(account.address); const signedTx = tx.sign(account, { nonce }); await signedTx .send(async result => { if (result.status.isFinalized === true && result.events !== undefined) { result.events.forEach(event => { if (event.event.method === 'ExtrinsicFailed') { if (expectFailure) { resolve(); } else { reject(new Error('Extrinsic failed unexpectedly')); } } }); resolve(); } if (result.status.isFuture) { console.log('nonce ' + nonce + ' for account ' + account.address + ' is in future'); this.clearNonce(account.address); reject(new Error('Extrinsic nonce is in future')); } }) .catch(error => { reject(error); }); }); } }