git.delta.rocks / unique-network / refs/commits / 4e863a28e4ae

difftreelog

source

tests/src/substrate/substrate-api.ts7.5 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {ApiPromise, WsProvider} from '@polkadot/api';18import {ApiOptions, ApiTypes, SubmittableExtrinsic} from '@polkadot/api/types';19import {ExtrinsicStatus} from '@polkadot/types/interfaces/author/types';20import {EventRecord} from '@polkadot/types/interfaces/system/types';21import {IKeyringPair} from '@polkadot/types/types';22import config from '../config';23import '../interfaces/augment-api-events';24import * as defs from '../interfaces/definitions';25import privateKey from './privateKey';26import promisifySubstrate from './promisify-substrate';27282930function defaultApiOptions(): ApiOptions {31  const wsProvider = new WsProvider(config.substrateUrl);32  return {33    provider: wsProvider, signedExtensions: {34      ContractHelpers: {35        extrinsic: {},36        payload: {},37      },38      FakeTransactionFinalizer: {39        extrinsic: {},40        payload: {},41      },42    },43    rpc: {44      unique: defs.unique.rpc,45      rmrk: defs.rmrk.rpc,46      eth: {47        feeHistory: {48          description: 'Dummy',49          params: [],50          type: 'u8',51        },52        maxPriorityFeePerGas: {53          description: 'Dummy',54          params: [],55          type: 'u8',56        },57      },58    },59  };60}6162export async function getApiConnection(settings: ApiOptions | undefined = undefined): Promise<ApiPromise> {63  settings = settings || defaultApiOptions();64  const api = new ApiPromise(settings);6566  if (api) {67    await api.isReadyOrError;68  }6970  return api;71}7273export default async function usingApi<T = void>(action: (api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair) => Promise<T>, settings: ApiOptions | undefined = undefined): Promise<T> {74  settings = settings || defaultApiOptions();75  const api: ApiPromise = new ApiPromise(settings);76  let result: T = null as unknown as T;7778  // TODO: Remove, this is temporary: Filter unneeded API output79  // (Jaco promised it will be removed in the next version)80  const consoleErr = console.error;81  const consoleLog = console.log;82  const consoleWarn = console.warn;8384  const outFn = (printer: any) => (...args: any[]) => {85    for (const arg of args) {86      if (typeof arg !== 'string')87        continue;88      if (arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis:') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure')89        return;90    }91    printer(...args);92  };9394  console.error = outFn(consoleErr.bind(console));95  console.log = outFn(consoleLog.bind(console));96  console.warn = outFn(consoleWarn.bind(console));9798  try {99    await promisifySubstrate(api, async () => {100      if (api) {101        await api.isReadyOrError;102        const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;103        const privateKeyWrapper = (account: string) => privateKey(account, Number(ss58Format));104        result = await action(api, privateKeyWrapper);105      }106    })();107  } finally {108    await api.disconnect();109    console.error = consoleErr;110    console.log = consoleLog;111    console.warn = consoleWarn;112  }113  return result as T;114}115116enum TransactionStatus {117  Success,118  Fail,119  NotReady120}121122function getTransactionStatus(events: EventRecord[], status: ExtrinsicStatus): TransactionStatus {123  if (status.isReady) {124    return TransactionStatus.NotReady;125  }126  if (status.isBroadcast) {127    return TransactionStatus.NotReady;128  }129  if (status.isRetracted) {130    return TransactionStatus.NotReady;131  }132  if (status.isInBlock || status.isFinalized) {133    if(events.filter(e => e.event.data.method === 'ExtrinsicFailed').length > 0) {134      return TransactionStatus.Fail;135    }136    if(events.filter(e => e.event.data.method === 'ExtrinsicSuccess').length > 0) {137      return TransactionStatus.Success;138    }139  }140141  return TransactionStatus.Fail;142}143144export function executeTransaction(api: ApiPromise, sender: IKeyringPair, transaction: SubmittableExtrinsic<'promise'>): Promise<EventRecord[]> {145  return new Promise(async (res, rej) => {146    try {147      await transaction.signAndSend(sender, ({events, status}) => {148        if (!status.isInBlock && !status.isFinalized) return;149        for (const {event} of events) {150          if (api.events.system.ExtrinsicSuccess.is(event)) {151            res(events);152          } else if (api.events.system.ExtrinsicFailed.is(event)) {153            const {data: [error]} = event;154            if (error.isModule) {155              const decoded = api.registry.findMetaError(error.asModule);156              const {method, section} = decoded;157              rej(new Error(`${section}.${method}`));158            } else {159              rej(new Error(error.toString()));160            }161          }162        }163      });164    } catch (e) {165      rej(e);166    }167  });168}169170export function171submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {172  /* eslint no-async-promise-executor: "off" */173  return new Promise(async (resolve, reject) => {174    try {175      await transaction.signAndSend(sender, ({events = [], status, dispatchError}) => {176        const transactionStatus = getTransactionStatus(events, status);177178        if (transactionStatus === TransactionStatus.Success) {179          resolve(events);180        } else if (transactionStatus === TransactionStatus.Fail) {181          let moduleError = null;182183          if (dispatchError) {184            if (dispatchError.isModule) {185              const modErr = dispatchError.asModule;186              const errorMeta = dispatchError.registry.findMetaError(modErr);187188              moduleError = JSON.stringify(errorMeta, null, 4);189            }190          }191192          console.log(`Something went wrong with transaction. Status: ${status}\nModule error: ${moduleError}`);193          reject(events);194        }195      });196    } catch (e) {197      console.log('Error: ', e);198      reject(e);199    }200  });201}202203export function submitTransactionExpectFailAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {204  console.error = () => {};205  console.log = () => {};206207  /* eslint no-async-promise-executor: "off" */208  return new Promise<EventRecord[]>(async function(res, rej) {209    const resolve = (rec: EventRecord[]) => {210      setTimeout(() => {211        res(rec);212      });213    };214    const reject = (errror: any) => {215      setTimeout(() => {216        rej(errror);217      });218    };219    try {220      await transaction.signAndSend(sender, ({events = [], status}) => {221        const transactionStatus = getTransactionStatus(events, status);222223        // console.log('transactionStatus', transactionStatus, 'events', events);224225        if (transactionStatus === TransactionStatus.Success) {226          resolve(events);227        } else if (transactionStatus === TransactionStatus.Fail) {228          reject(events);229        }230      });231    } catch (e) {232      reject(e);233    }234  });235}