103 lines
2.6 KiB
TypeScript
103 lines
2.6 KiB
TypeScript
|
import type { ethers } from 'ethers';
|
||
|
import { useCallback, useState } from 'react';
|
||
|
import { EthereumError, isEthereumError } from './ethereum-error';
|
||
|
|
||
|
export enum EthTxStatus {
|
||
|
Default = 'Default',
|
||
|
Requested = 'Requested',
|
||
|
Pending = 'Pending',
|
||
|
Complete = 'Complete',
|
||
|
Error = 'Error',
|
||
|
}
|
||
|
|
||
|
export type TxError = Error | EthereumError;
|
||
|
|
||
|
export interface EthTxState {
|
||
|
status: EthTxStatus;
|
||
|
error: TxError | null;
|
||
|
txHash: string | null;
|
||
|
receipt: ethers.ContractReceipt | null;
|
||
|
confirmations: number;
|
||
|
}
|
||
|
|
||
|
export const initialState = {
|
||
|
status: EthTxStatus.Default,
|
||
|
error: null,
|
||
|
txHash: null,
|
||
|
receipt: null,
|
||
|
confirmations: 0,
|
||
|
};
|
||
|
|
||
|
export const useEthereumTransaction = <TArgs = void>(
|
||
|
performTransaction: (
|
||
|
args: TArgs
|
||
|
) => Promise<ethers.ContractTransaction> | null,
|
||
|
requiredConfirmations = 1
|
||
|
) => {
|
||
|
const [transaction, _setTransaction] = useState<EthTxState>(initialState);
|
||
|
|
||
|
const setTransaction = useCallback((update: Partial<EthTxState>) => {
|
||
|
_setTransaction((curr) => ({
|
||
|
...curr,
|
||
|
...update,
|
||
|
}));
|
||
|
}, []);
|
||
|
|
||
|
const perform = useCallback(
|
||
|
async (args: TArgs) => {
|
||
|
setTransaction({
|
||
|
status: EthTxStatus.Requested,
|
||
|
error: null,
|
||
|
confirmations: 0,
|
||
|
});
|
||
|
|
||
|
try {
|
||
|
const res = performTransaction(args);
|
||
|
|
||
|
if (res === null) {
|
||
|
setTransaction({ status: EthTxStatus.Default });
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const tx = await res;
|
||
|
|
||
|
let receipt: ethers.ContractReceipt | null = null;
|
||
|
|
||
|
setTransaction({ status: EthTxStatus.Pending, txHash: tx.hash });
|
||
|
|
||
|
for (let i = 1; i <= requiredConfirmations; i++) {
|
||
|
receipt = await tx.wait(i);
|
||
|
setTransaction({ confirmations: receipt.confirmations });
|
||
|
}
|
||
|
|
||
|
if (!receipt) {
|
||
|
throw new Error('No receipt after confirmations are met');
|
||
|
}
|
||
|
|
||
|
setTransaction({ status: EthTxStatus.Complete, receipt });
|
||
|
} catch (err) {
|
||
|
if (err instanceof Error) {
|
||
|
setTransaction({ status: EthTxStatus.Error, error: err });
|
||
|
} else if (isEthereumError(err)) {
|
||
|
setTransaction({
|
||
|
status: EthTxStatus.Error,
|
||
|
error: new EthereumError(err.message, err.code),
|
||
|
});
|
||
|
} else {
|
||
|
setTransaction({
|
||
|
status: EthTxStatus.Error,
|
||
|
error: new Error('Something went wrong'),
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
[performTransaction, requiredConfirmations, setTransaction]
|
||
|
);
|
||
|
|
||
|
const reset = useCallback(() => {
|
||
|
setTransaction(initialState);
|
||
|
}, [setTransaction]);
|
||
|
|
||
|
return { perform, transaction, reset };
|
||
|
};
|