@smartinvoicexyz/hooks
Version:
Unified source for React hooks used across the Smart Invoice protocol.
98 lines (97 loc) • 4.18 kB
JavaScript
import { INVOICE_VERSION, SMART_INVOICE_UPDATABLE_ABI, } from '@smartinvoicexyz/constants';
import { waitForSubgraphSync } from '@smartinvoicexyz/graphql';
import { errorToastHandler, getDateString, uriToDocument, } from '@smartinvoicexyz/utils';
import _ from 'lodash';
import { useCallback, useMemo, useState } from 'react';
import { parseUnits } from 'viem';
import { usePublicClient, useSimulateContract, useWriteContract } from 'wagmi';
import { useDetailsPin } from './useDetailsPin';
export const useResolve = ({ invoice, localForm, onTxSuccess, toast, details, }) => {
const publicClient = usePublicClient();
const { document, description, clientAward: clientAwardForm, providerAward: providerAwardForm, resolverAward: resolverAwardForm, } = localForm.getValues();
const { tokenBalance, address, isLocked, tokenMetadata, metadata } = _.pick(invoice, ['tokenBalance', 'tokenMetadata', 'address', 'isLocked', 'metadata']);
const detailsData = useMemo(() => {
if (details) {
return null;
}
const now = Math.floor(new Date().getTime() / 1000);
const title = `Resolve ${metadata?.title} at ${getDateString(now)}`;
return {
version: INVOICE_VERSION,
id: _.join([title, now, INVOICE_VERSION], '-'),
title,
description,
documents: document ? [uriToDocument(document)] : [],
createdAt: now,
};
}, [description, document, metadata, details]);
const { data: detailsHash, isLoading: detailsLoading } = useDetailsPin(detailsData, true);
const clientAward = clientAwardForm && tokenMetadata?.decimals
? parseUnits(clientAwardForm.toString(), tokenMetadata.decimals)
: BigInt(0);
const providerAward = providerAwardForm && tokenMetadata?.decimals
? parseUnits(providerAwardForm.toString(), tokenMetadata.decimals)
: BigInt(0);
const resolverAward = resolverAwardForm && tokenMetadata?.decimals
? parseUnits(resolverAwardForm.toString(), tokenMetadata.decimals)
: BigInt(0);
const fullBalance = tokenBalance?.value === clientAward + providerAward + resolverAward;
const { data, isLoading: prepareLoading, error: prepareError, } = useSimulateContract({
address: address,
functionName: 'resolve',
abi: SMART_INVOICE_UPDATABLE_ABI,
args: [clientAward, providerAward, details ?? detailsHash],
query: {
enabled: !!address &&
fullBalance &&
isLocked &&
tokenBalance.value > BigInt(0) &&
(!!details || !!detailsHash) &&
!!description,
},
});
const [waitingForTx, setWaitingForTx] = useState(false);
const { writeContractAsync, isPending: writeLoading, error: writeError, } = useWriteContract({
mutation: {
onSuccess: async (hash) => {
setWaitingForTx(true);
const receipt = await publicClient?.waitForTransactionReceipt({
hash,
});
if (!receipt)
return;
if (receipt && publicClient) {
await waitForSubgraphSync(publicClient.chain.id, receipt.blockNumber);
}
setWaitingForTx(false);
onTxSuccess?.();
},
onError: error => {
if (toast)
errorToastHandler('useResolve', error, toast);
},
},
});
const writeAsync = useCallback(async () => {
try {
if (!data) {
throw new Error('simulation data is not available');
}
return writeContractAsync(data.request);
}
catch (error) {
if (toast)
errorToastHandler('useResolve', error, toast);
return undefined;
}
}, [writeContractAsync, data]);
return {
writeAsync,
isLoading: prepareLoading ||
writeLoading ||
waitingForTx ||
!(details || !detailsLoading),
prepareError,
writeError,
};
};