Skip to content

Commit

Permalink
Merge commit 'bfdad709af134d4c9dad9a0b542c6a43957519ba' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaw3d committed Sep 10, 2024
2 parents c16b7b8 + bfdad70 commit 2f7bd2b
Show file tree
Hide file tree
Showing 52 changed files with 2,964 additions and 2,037 deletions.
19 changes: 16 additions & 3 deletions packages/nextjs/components/NetworksDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactNode, useEffect, useRef, useState } from "react";
import Image from "next/image";
import * as chains from "@wagmi/core/chains";
import * as wagmiChains from "@wagmi/core/chains";
import { useTheme } from "next-themes";
import Select, { MultiValue, OptionProps, SingleValue, components } from "react-select";
import { Chain } from "viem";
Expand All @@ -22,6 +22,8 @@ type GroupedOptions = Record<
}
>;

type Chains = Record<string, Chain>;

const getIconComponent = (iconName: string | undefined) => {
switch (iconName) {
case "EyeIcon":
Expand Down Expand Up @@ -73,6 +75,17 @@ const groupedOptions = networks.reduce<GroupedOptions>(
},
);

const excludeChainKeys = ["lineaTestnet", "x1Testnet"]; // duplicate chains in viem chains

const unfilteredChains: Chains = wagmiChains as Chains;

const filteredChains = Object.keys(unfilteredChains)
.filter(key => !excludeChainKeys.includes(key))
.reduce((obj: Chains, key) => {
obj[key] = unfilteredChains[key];
return obj;
}, {} as Chains);

const filterChains = (
chains: Record<string, Chain>,
networkIds: Set<number>,
Expand Down Expand Up @@ -187,9 +200,9 @@ export const NetworksDropdown = ({ onChange }: { onChange: (options: any) => any
.filter(value => typeof value === "number") as number[],
);

const filteredChains = filterChains(chains, networkIds, existingChainIds);
const filteredChainsForModal = filterChains(filteredChains, networkIds, existingChainIds);

const modalChains = mapChainsToOptions(filteredChains).filter(chain =>
const modalChains = mapChainsToOptions(filteredChainsForModal).filter(chain =>
`${chain.label} ${chain.value}`.toLowerCase().includes(searchTerm.toLowerCase()),
);

Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/components/blockexplorer/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const SearchBar = () => {
event.preventDefault();
if (isHex(searchInput)) {
try {
const tx = await client.getTransaction({ hash: searchInput });
const tx = await client?.getTransaction({ hash: searchInput });
if (tx) {
router.push(`/blockexplorer/transaction/${searchInput}`);
return;
Expand Down
17 changes: 13 additions & 4 deletions packages/nextjs/components/scaffold-eth/Address.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AccountAvatar } from "../AccountAvatar";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { Address as AddressType, isAddress } from "viem";
import { hardhat } from "viem/chains";
import { normalize } from "viem/ens";
import { useEnsAvatar, useEnsName } from "wagmi";
import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline";
import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
Expand Down Expand Up @@ -35,12 +36,20 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }:

const { targetNetwork } = useTargetNetwork();

const { data: fetchedEns } = useEnsName({ address, enabled: isAddress(address ?? ""), chainId: 1 });
const { data: fetchedEns } = useEnsName({
address: address,
chainId: 1,
query: {
enabled: isAddress(address ?? ""),
},
});
const { data: fetchedEnsAvatar } = useEnsAvatar({
name: fetchedEns,
enabled: Boolean(fetchedEns),
name: fetchedEns ? normalize(fetchedEns) : undefined,
chainId: 1,
cacheTime: 30_000,
query: {
enabled: Boolean(fetchedEns),
gcTime: 30_000,
},
});

// We need to apply this pattern to avoid Hydration errors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { useEffect } from "react";
import { InheritanceTooltip } from "./InheritanceTooltip";
import { Abi, AbiFunction } from "abitype";
import { Address } from "viem";
import { useContractRead } from "wagmi";
import { useReadContract } from "wagmi";
import { ArrowPathIcon } from "@heroicons/react/24/outline";
import { displayTxResult } from "~~/components/scaffold-eth";
import { useAnimationConfig } from "~~/hooks/scaffold-eth";
import { useAbiNinjaState } from "~~/services/store/store";
import { notification } from "~~/utils/scaffold-eth";
import { getParsedError, notification } from "~~/utils/scaffold-eth";

type DisplayVariableProps = {
contractAddress: Address;
Expand All @@ -29,16 +29,21 @@ export const DisplayVariable = ({
data: result,
isFetching,
refetch,
} = useContractRead({
error,
} = useReadContract({
address: contractAddress,
functionName: abiFunction.name,
abi: abi,
chainId: mainChainId,
onError: error => {
notification.error(error.message);
},
});

useEffect(() => {
if (error) {
const parsedError = getParsedError(error);
notification.error(parsedError);
}
}, [error]);

const { showAnimation } = useAnimationConfig(result);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { type KeyboardEvent, useState } from "react";
import { AugmentedAbiFunction } from "./ContractUI";
import { ChevronDownIcon, ChevronRightIcon, XMarkIcon } from "@heroicons/react/24/outline";

Expand Down Expand Up @@ -30,6 +30,14 @@ export const MethodSelector = ({
return abi.some(method => method.uid === uid);
};

const callOnMethodSelectOnSpaceOrEnter = (event: KeyboardEvent<HTMLDivElement>, uid: string) => {
if (event.key === " " || event.key === "Enter") {
event.preventDefault();
onMethodSelect(uid);
event.stopPropagation();
}
};

return (
<div className="overflow-auto h-[80vh]">
<input id="sidebar" type="checkbox" className="drawer-toggle" />
Expand All @@ -54,22 +62,31 @@ export const MethodSelector = ({
<div className="flex flex-col items-start gap-1 pb-4">
{readMethods.map(method => (
<div key={method.uid} className="flex items-center gap-2 w-full pr-4">
<button
<div
role="button"
tabIndex={0}
className={`btn btn-sm btn-ghost font-normal pr-1 w-full justify-between ${
isMethodSelected(method.uid) ? "bg-neutral pointer-events-none" : ""
}`}
onClick={() => onMethodSelect(method.uid)}
onClick={() => {
onMethodSelect(method.uid);
}}
onKeyDown={event => callOnMethodSelectOnSpaceOrEnter(event, method.uid)}
>
{method.name}
{isMethodSelected(method.uid) && (
<button
className="ml-4 text-xs hover:bg-base-100 rounded-md p-1 pointer-events-auto"
onClick={() => removeMethod(method.uid)}
onClick={event => {
removeMethod(method.uid);
event.stopPropagation();
}}
onKeyDown={event => event.stopPropagation()}
>
<XMarkIcon className="h-4 w-4" />
</button>
)}
</button>
</div>
</div>
))}
</div>
Expand All @@ -93,23 +110,30 @@ export const MethodSelector = ({
<div className="flex flex-col items-start gap-1">
{writeMethods.map((method, index) => (
<div key={index} className="flex items-center gap-2 w-full pr-4">
<button
<div
role="button"
tabIndex={0}
className={`btn btn-sm btn-ghost font-normal pr-1 w-full justify-between ${
isMethodSelected(method.uid) ? "bg-neutral pointer-events-none" : ""
}`}
onKeyDown={event => callOnMethodSelectOnSpaceOrEnter(event, method.uid)}
onClick={() => onMethodSelect(method.uid)}
>
{method.name}
{/* Warning: validateDOMNesting(...): <button> cannot appear as a descendant of <button>. */}
{isMethodSelected(method.uid) && (
<button
className="ml-4 text-xs hover:bg-base-100 rounded-md p-1 pointer-events-auto"
onClick={() => removeMethod(method.uid)}
onClick={event => {
removeMethod(method.uid);
event.stopPropagation();
}}
onKeyDown={event => event.stopPropagation()}
>
<XMarkIcon className="h-4 w-4" />
</button>
)}
</button>
</div>
</div>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { InheritanceTooltip } from "./InheritanceTooltip";
import { Abi, AbiFunction } from "abitype";
import { Address } from "viem";
import { useContractRead } from "wagmi";
import { useReadContract } from "wagmi";
import {
ContractInput,
displayTxResult,
getFunctionInputKey,
getInitialFormState,
getParsedContractFunctionArgs,
getParsedError,
transformAbiFunction,
} from "~~/components/scaffold-eth";
import { useAbiNinjaState } from "~~/services/store/store";
import { notification } from "~~/utils/scaffold-eth";
import { getParsedError, notification } from "~~/utils/scaffold-eth";

type ReadOnlyFunctionFormProps = {
contractAddress: Address;
Expand All @@ -32,19 +31,25 @@ export const ReadOnlyFunctionForm = ({
const [form, setForm] = useState<Record<string, any>>(() => getInitialFormState(abiFunction));
const [result, setResult] = useState<unknown>();

const { isFetching, refetch } = useContractRead({
const { isFetching, refetch, error } = useReadContract({
address: contractAddress,
functionName: abiFunction.name,
abi: abi,
args: getParsedContractFunctionArgs(form),
enabled: false,
chainId: mainChainId,
onError: (error: any) => {
const parsedErrror = getParsedError(error);
notification.error(parsedErrror);
query: {
enabled: false,
retry: false,
},
});

useEffect(() => {
if (error) {
const parsedError = getParsedError(error);
notification.error(parsedError);
}
}, [error]);

const transformedFunction = transformAbiFunction(abiFunction);
const inputElements = transformedFunction.inputs.map((input, inputIndex) => {
const key = getFunctionInputKey(abiFunction.name, input, inputIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,18 @@ import { InheritanceTooltip } from "./InheritanceTooltip";
import { useConnectModal } from "@rainbow-me/rainbowkit";
import { Abi, AbiFunction } from "abitype";
import { Address, TransactionReceipt } from "viem";
import { useAccount, useContractWrite, useNetwork, useWaitForTransaction, useWalletClient } from "wagmi";
import { useAccount, useWaitForTransactionReceipt, useWriteContract } from "wagmi";
import {
ContractInput,
IntegerInput,
TxReceipt,
getFunctionInputKey,
getInitialFormState,
getParsedContractFunctionArgs,
getParsedError,
transformAbiFunction,
tryConvertingToBigInt,
} from "~~/components/scaffold-eth";
import { useTransactor } from "~~/hooks/scaffold-eth";
import { useAbiNinjaState } from "~~/services/store/store";
import { notification } from "~~/utils/scaffold-eth";

type WriteOnlyFunctionFormProps = {
abi: Abi;
Expand All @@ -37,48 +34,37 @@ export const WriteOnlyFunctionForm = ({
const mainChainId = useAbiNinjaState(state => state.mainChainId);
const [form, setForm] = useState<Record<string, any>>(() => getInitialFormState(abiFunction));
const [txValue, setTxValue] = useState<string | bigint>("");
const { chain } = useNetwork();
const { chain } = useAccount();
const writeTxn = useTransactor();
const { address: connectedAddress } = useAccount();
const { openConnectModal } = useConnectModal();
const wrongNetwork = !chain || chain?.id !== mainChainId;
const walletClient = useWalletClient({ chainId: mainChainId });

const {
data: result,
isLoading,
writeAsync,
} = useContractWrite({
mode: "prepared", // Workaround to avoid simulating and throwing e.g. "execution reverted: ERC20: burn from the zero address"
request: {
account: walletClient.data?.account ?? "",
address: contractAddress,
functionName: abiFunction.name,
chainId: mainChainId,
chain: { id: mainChainId } as any,
abi: abi,
args: getParsedContractFunctionArgs(form),
value: tryConvertingToBigInt(txValue, 0n),
},
});
const { data: result, isPending, writeContractAsync } = useWriteContract();

const handleWrite = async () => {
if (writeAsync) {
if (writeContractAsync) {
try {
BigInt(txValue); // Ensure no ignored errors from tryConvertingToBigInt
const makeWriteWithParams = () => writeAsync();
const makeWriteWithParams = () =>
writeContractAsync({
address: contractAddress,
functionName: abiFunction.name,
abi: abi,
chainId: mainChainId,
args: getParsedContractFunctionArgs(form),
value: BigInt(txValue),
});
await writeTxn(makeWriteWithParams);
onChange();
} catch (e: any) {
const message = getParsedError(e);
notification.error(message);
console.error("⚡️ ~ file: WriteOnlyFunctionForm.tsx:handleWrite ~ error", e);
}
}
};

const [displayedTxResult, setDisplayedTxResult] = useState<TransactionReceipt>();
const { data: txResult } = useWaitForTransaction({
hash: result?.hash,
const { data: txResult } = useWaitForTransactionReceipt({
hash: result,
});
useEffect(() => {
setDisplayedTxResult(txResult);
Expand Down Expand Up @@ -141,8 +127,8 @@ export const WriteOnlyFunctionForm = ({
}`}
data-tip={`${wrongNetwork && "Wrong network"}`}
>
<button className="btn btn-secondary btn-sm" disabled={wrongNetwork || isLoading} onClick={handleWrite}>
{isLoading && <span className="loading loading-spinner loading-xs"></span>}
<button className="btn btn-secondary btn-sm" disabled={wrongNetwork || isPending} onClick={handleWrite}>
{isPending && <span className="loading loading-spinner loading-xs"></span>}
Send 💸
</button>
</div>
Expand Down
Loading

0 comments on commit 2f7bd2b

Please sign in to comment.