145 lines
5.0 KiB
TypeScript
145 lines
5.0 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { InheritanceTooltip } from "./InheritanceTooltip";
|
|
import { Abi, AbiFunction } from "abitype";
|
|
import { Address, TransactionReceipt } from "viem";
|
|
import { useAccount, useConfig, useWaitForTransactionReceipt, useWriteContract } from "wagmi";
|
|
import {
|
|
ContractInput,
|
|
TxReceipt,
|
|
getFunctionInputKey,
|
|
getInitialFormState,
|
|
getParsedContractFunctionArgs,
|
|
transformAbiFunction,
|
|
} from "~~/app/debug/_components/contract";
|
|
import { IntegerInput } from "~~/components/scaffold-eth";
|
|
import { useTransactor } from "~~/hooks/scaffold-eth";
|
|
import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
|
|
import { simulateContractWriteAndNotifyError } from "~~/utils/scaffold-eth/contract";
|
|
|
|
type WriteOnlyFunctionFormProps = {
|
|
abi: Abi;
|
|
abiFunction: AbiFunction;
|
|
onChange: () => void;
|
|
contractAddress: Address;
|
|
inheritedFrom?: string;
|
|
};
|
|
|
|
export const WriteOnlyFunctionForm = ({
|
|
abi,
|
|
abiFunction,
|
|
onChange,
|
|
contractAddress,
|
|
inheritedFrom,
|
|
}: WriteOnlyFunctionFormProps) => {
|
|
const [form, setForm] = useState<Record<string, any>>(() => getInitialFormState(abiFunction));
|
|
const [txValue, setTxValue] = useState<string>("");
|
|
const { chain } = useAccount();
|
|
const writeTxn = useTransactor();
|
|
const { targetNetwork } = useTargetNetwork();
|
|
const writeDisabled = !chain || chain?.id !== targetNetwork.id;
|
|
|
|
const { data: result, isPending, writeContractAsync } = useWriteContract();
|
|
|
|
const wagmiConfig = useConfig();
|
|
|
|
const handleWrite = async () => {
|
|
if (writeContractAsync) {
|
|
try {
|
|
const writeContractObj = {
|
|
address: contractAddress,
|
|
functionName: abiFunction.name,
|
|
abi: abi,
|
|
args: getParsedContractFunctionArgs(form),
|
|
value: BigInt(txValue),
|
|
};
|
|
await simulateContractWriteAndNotifyError({ wagmiConfig, writeContractParams: writeContractObj });
|
|
|
|
const makeWriteWithParams = () => writeContractAsync(writeContractObj);
|
|
await writeTxn(makeWriteWithParams);
|
|
onChange();
|
|
} catch (e: any) {
|
|
console.error("⚡️ ~ file: WriteOnlyFunctionForm.tsx:handleWrite ~ error", e);
|
|
}
|
|
}
|
|
};
|
|
|
|
const [displayedTxResult, setDisplayedTxResult] = useState<TransactionReceipt>();
|
|
const { data: txResult } = useWaitForTransactionReceipt({
|
|
hash: result,
|
|
});
|
|
useEffect(() => {
|
|
setDisplayedTxResult(txResult);
|
|
}, [txResult]);
|
|
|
|
// TODO use `useMemo` to optimize also update in ReadOnlyFunctionForm
|
|
const transformedFunction = transformAbiFunction(abiFunction);
|
|
const inputs = transformedFunction.inputs.map((input, inputIndex) => {
|
|
const key = getFunctionInputKey(abiFunction.name, input, inputIndex);
|
|
return (
|
|
<ContractInput
|
|
key={key}
|
|
setForm={updatedFormValue => {
|
|
setDisplayedTxResult(undefined);
|
|
setForm(updatedFormValue);
|
|
}}
|
|
form={form}
|
|
stateObjectKey={key}
|
|
paramType={input}
|
|
/>
|
|
);
|
|
});
|
|
const zeroInputs = inputs.length === 0 && abiFunction.stateMutability !== "payable";
|
|
|
|
return (
|
|
<div className="py-5 space-y-3 first:pt-0 last:pb-1">
|
|
<div className={`flex gap-3 ${zeroInputs ? "flex-row justify-between items-center" : "flex-col"}`}>
|
|
<p className="font-medium my-0 break-words">
|
|
{abiFunction.name}
|
|
<InheritanceTooltip inheritedFrom={inheritedFrom} />
|
|
</p>
|
|
{inputs}
|
|
{abiFunction.stateMutability === "payable" ? (
|
|
<div className="flex flex-col gap-1.5 w-full">
|
|
<div className="flex items-center ml-2">
|
|
<span className="text-xs font-medium mr-2 leading-none">payable value</span>
|
|
<span className="block text-xs font-extralight leading-none">wei</span>
|
|
</div>
|
|
<IntegerInput
|
|
value={txValue}
|
|
onChange={updatedTxValue => {
|
|
setDisplayedTxResult(undefined);
|
|
setTxValue(updatedTxValue);
|
|
}}
|
|
placeholder="value (wei)"
|
|
/>
|
|
</div>
|
|
) : null}
|
|
<div className="flex justify-between gap-2">
|
|
{!zeroInputs && (
|
|
<div className="grow basis-0">{displayedTxResult ? <TxReceipt txResult={displayedTxResult} /> : null}</div>
|
|
)}
|
|
<div
|
|
className={`flex ${
|
|
writeDisabled &&
|
|
"tooltip tooltip-bottom tooltip-secondary before:content-[attr(data-tip)] before:-translate-x-1/3 before:left-auto before:transform-none"
|
|
}`}
|
|
data-tip={`${writeDisabled && "Wallet not connected or in the wrong network"}`}
|
|
>
|
|
<button className="btn btn-secondary btn-sm" disabled={writeDisabled || isPending} onClick={handleWrite}>
|
|
{isPending && <span className="loading loading-spinner loading-xs"></span>}
|
|
Send 💸
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{zeroInputs && txResult ? (
|
|
<div className="grow basis-0">
|
|
<TxReceipt txResult={txResult} />
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
};
|