kodadot/nft-gallery

View on GitHub
composables/useMetaTransaction.ts

Summary

Maintainability
B
5 hrs
Test Coverage
import type { DispatchError } from '@polkadot/types/interfaces'
import type { ISubmittableResult } from '@polkadot/types/types'
import useAPI from './useApi'
import useTransactionStatus from './useTransactionStatus'
import exec, {
  execResultValue,
  txCb,
} from '@/utils/transactionExecutor'
import type {
  ExecResult,
  Extrinsic,
  TxCbOnSuccessParams } from '@/utils/transactionExecutor'

export type HowAboutToExecuteOnSuccessParam = {
  txHash: string
  blockNumber: string
}

export type HowAboutToExecuteOnResultParam = {
  txHash: string
  result: ISubmittableResult
}

type HowAboutToExecuteOptions = {
  onSuccess?: (param: HowAboutToExecuteOnSuccessParam) => void
  onError?: () => void
  onResult?: (result: HowAboutToExecuteOnResultParam) => void
}

export type HowAboutToExecute = (
  account: string,
  cb: (...params: any[]) => Extrinsic,
  args: any[],
  options?: HowAboutToExecuteOptions
) => Promise<void>

function useMetaTransaction() {
  const { $i18n } = useNuxtApp()
  const {
    isLoading,
    resolveStatus,
    initTransactionLoader,
    status,
    stopLoader,
  } = useTransactionStatus()
  const { apiInstance } = useAPI()
  const tx = ref<ExecResult>()
  /**
   *  Indicates whether an error occurred during the transaction.
   *  This can be true if:
   *  - {@link TransactionStatus.Unknown} Invalid Transaction (e.g., inability to pay some fees)
   *  - {@link TransactionStatus.Block}
   */
  const isError = ref(false)

  const howAboutToExecute: HowAboutToExecute = async (
    account,
    cb,
    args,
    {
      onSuccess,
      onError,
      onResult,
    } = {},
  ): Promise<void> => {
    try {
      tx.value = await exec(
        account,
        '',
        cb,
        args,
        txCb(successCb(onSuccess), errorCb(onError), resultCb(onResult)),
      )
    }
    catch (e) {
      onCatchError(e)
    }
  }

  const successCb
    = (onSuccess?: (param: HowAboutToExecuteOnSuccessParam) => void) =>
      async ({ blockHash, txHash }: TxCbOnSuccessParams) => {
        const api = await apiInstance.value

        tx.value && execResultValue(tx.value)
        const header = await api.rpc.chain.getHeader(blockHash)
        const blockNumber = header.number.toString()

        if (onSuccess) {
          onSuccess({ txHash: txHash.toString(), blockNumber })
        }

        isLoading.value = false
        tx.value = undefined
      }

  const errorCb = onError => (dispatchError) => {
    tx.value && execResultValue(tx.value)
    onTxError(dispatchError)
    isLoading.value = false
    isError.value = true
    if (onError) {
      onError()
    }
  }

  const resultCb
    = (onResult?: (result: HowAboutToExecuteOnResultParam) => void) =>
      (result: ISubmittableResult) => {
        resolveStatus(result.status)
        onResult?.({ txHash: result.txHash.toString(), result })
      }

  const onCatchError = (e) => {
    if (e instanceof Error) {
      const errorMessage = e.message?.toLowerCase() || ''
      const isCancelled = errorMessage.includes('cancelled') || errorMessage.includes('rejected')
      if (isCancelled) {
        warningMessage($i18n.t('general.tx.cancelled'), { reportable: false })

        status.value = TransactionStatus.Cancelled
      }
      else {
        isError.value = true
        warningMessage(e.toString())
      }
      isLoading.value = false
      tx.value = undefined
    }
  }
  const onTxError = async (dispatchError: DispatchError): Promise<void> => {
    await notifyDispatchError(dispatchError)
    isLoading.value = false
    tx.value = undefined
  }
  return {
    howAboutToExecute,
    onTxError,
    initTransactionLoader,
    status,
    isLoading,
    stopLoader,
    isError,
  }
}

export default useMetaTransaction