kodadot/nft-gallery

View on GitHub
composables/transaction/evm/useMetaTransaction.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { type Address } from 'viem'
import { simulateContract, waitForTransactionReceipt, writeContract } from '@wagmi/core'
import { useChainId, useConfig, useSwitchChain } from '@wagmi/vue'
import type { Abi } from '../types'
import useTransactionStatus from '@/composables/useTransactionStatus'

type EvmHowAboutToExecuteEvents = {
  onSuccess?: (param: EvmHowAboutToExecuteOnSuccessParam) => void
  onError?: () => void
}

export type EvmHowAboutToExecuteParam = {
  account: Address
  address: Address
  functionName: string
  abi: Abi
  args: unknown[]
  value?: string
} & EvmHowAboutToExecuteEvents

export type EvmHowAboutToExecute = (
  params: EvmHowAboutToExecuteParam,
) => Promise<void>

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

/*
 TODO
 unify useMetaTransaction api between evms
*/
export default function useEvmMetaTransaction() {
  const { $i18n } = useNuxtApp()
  const { isLoading, initTransactionLoader, status, stopLoader } = useTransactionStatus()
  const { switchChainAsync: switchChain } = useSwitchChain()
  const { urlPrefix: prefix } = usePrefix()
  const chainId = useChainId()
  const wagmiConfig = useConfig()

  const tx = ref<ExecResult>()
  const isError = ref(false)

  const howAboutToExecute: EvmHowAboutToExecute = async ({
    account,
    functionName,
    address,
    args,
    abi,
    value,
    onSuccess,
    onError,
  }: EvmHowAboutToExecuteParam): Promise<void> => {
    try {
      await syncWalletChain()

      const { request } = await simulateContract(wagmiConfig, {
        account,
        address,
        abi,
        args,
        functionName,
        value: value ? BigInt(value) : undefined,
      })

      const txHash = await writeContract(wagmiConfig, request)
      console.log('[EXEC] Executed', txHash)
      status.value = TransactionStatus.Broadcast

      const transaction = await waitForTransactionReceipt(wagmiConfig, { hash: txHash })
      console.log('[EXEC] Completed', transaction)

      const map = {
        success: () => successCb(onSuccess)({ txHash, blockNumber: transaction.blockNumber.toString() }),
        reverted: () => errorCb(onError)({ error: new Error('Transaction reverted') }),
      }

      map[transaction.status]?.()
    }
    catch (e) {
      errorCb(onError)({ error: e })
    }
  }

  const syncWalletChain = async () => {
    const chain = PREFIX_TO_CHAIN[prefix.value]
    if (chain && chainId.value !== chain.id) {
      await switchChain({ chainId: chain.id })
    }
  }

  const successCb
    = (onSuccess?: (param: EvmHowAboutToExecuteOnSuccessParam) => void) =>
      async ({ txHash, blockNumber }) => {
        if (onSuccess) {
          onSuccess({ txHash: txHash, blockNumber: blockNumber })
        }

        status.value = TransactionStatus.Finalized
        isLoading.value = false
        tx.value = undefined
      }

  const errorCb
    = (onError?: () => void) =>
      ({ error }) => {
        if (error.message?.includes('User rejected the request.')) {
          warningMessage($i18n.t('general.tx.cancelled'), { reportable: false })
          status.value = TransactionStatus.Cancelled
        }
        else {
          isError.value = true
          warningMessage(error.toString())
          if (onError) {
            onError()
          }
        }
        isLoading.value = false
        tx.value = undefined
      }

  return {
    howAboutToExecute,
    initTransactionLoader,
    status,
    isLoading,
    stopLoader,
    isError,
  }
}