Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional chain parameter in Scaffold hooks #931

Merged
merged 18 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type ContractUIProps = {
export const ContractUI = ({ contractName, className = "" }: ContractUIProps) => {
const [refreshDisplayVariables, triggerRefreshDisplayVariables] = useReducer(value => !value, false);
const { targetNetwork } = useTargetNetwork();
const { data: deployedContractData, isLoading: deployedContractLoading } = useDeployedContractInfo(contractName);
const { data: deployedContractData, isLoading: deployedContractLoading } = useDeployedContractInfo({ contractName });
const networkColor = useNetworkColor();

if (deployedContractLoading) {
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/hooks/scaffold-eth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from "./useScaffoldWriteContract";
export * from "./useTargetNetwork";
export * from "./useTransactor";
export * from "./useWatchBalance";
export * from "./useSelectedNetwork";
22 changes: 16 additions & 6 deletions packages/nextjs/hooks/scaffold-eth/useDeployedContractInfo.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import { useEffect, useState } from "react";
import { useTargetNetwork } from "./useTargetNetwork";
import { useIsMounted } from "usehooks-ts";
import { usePublicClient } from "wagmi";
import { Contract, ContractCodeStatus, ContractName, contracts } from "~~/utils/scaffold-eth/contract";
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
import {
Contract,
ContractCodeStatus,
ContractName,
UseDeployedContractConfig,
contracts,
} from "~~/utils/scaffold-eth/contract";

/**
* Gets the matching contract info for the provided contract name from the contracts present in deployedContracts.ts
* and externalContracts.ts corresponding to targetNetworks configured in scaffold.config.ts
*/
export const useDeployedContractInfo = <TContractName extends ContractName>(contractName: TContractName) => {
export const useDeployedContractInfo = <TContractName extends ContractName>({
contractName,
chainId,
}: UseDeployedContractConfig<TContractName>) => {
const isMounted = useIsMounted();
const { targetNetwork } = useTargetNetwork();
const deployedContract = contracts?.[targetNetwork.id]?.[contractName as ContractName] as Contract<TContractName>;

const selectedNetwork = useSelectedNetwork(chainId);
const deployedContract = contracts?.[selectedNetwork.id]?.[contractName as ContractName] as Contract<TContractName>;
const [status, setStatus] = useState<ContractCodeStatus>(ContractCodeStatus.LOADING);
const publicClient = usePublicClient({ chainId: targetNetwork.id });
const publicClient = usePublicClient({ chainId: selectedNetwork.id });

useEffect(() => {
const checkContractDeployment = async () => {
Expand Down
9 changes: 5 additions & 4 deletions packages/nextjs/hooks/scaffold-eth/useNetworkColor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useTargetNetwork } from "./useTargetNetwork";
import { useTheme } from "next-themes";
import { ChainWithAttributes } from "~~/utils/scaffold-eth";
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
import { AllowedChainIds, ChainWithAttributes } from "~~/utils/scaffold-eth";

export const DEFAULT_NETWORK_COLOR: [string, string] = ["#666666", "#bbbbbb"];

Expand All @@ -12,11 +13,11 @@ export function getNetworkColor(network: ChainWithAttributes, isDarkMode: boolea
/**
* Gets the color of the target network
*/
export const useNetworkColor = () => {
export const useNetworkColor = (chainId?: AllowedChainIds) => {
const { resolvedTheme } = useTheme();
const { targetNetwork } = useTargetNetwork();

const chain = useSelectedNetwork(chainId);
const isDarkMode = resolvedTheme === "dark";

return getNetworkColor(targetNetwork, isDarkMode);
return getNetworkColor(chain, isDarkMode);
};
16 changes: 12 additions & 4 deletions packages/nextjs/hooks/scaffold-eth/useScaffoldContract.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useTargetNetwork } from "./useTargetNetwork";
import { Account, Address, Chain, Client, Transport, getContract } from "viem";
import { usePublicClient } from "wagmi";
import { GetWalletClientReturnType } from "wagmi/actions";
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
import { useDeployedContractInfo } from "~~/hooks/scaffold-eth";
import { AllowedChainIds } from "~~/utils/scaffold-eth";
import { Contract, ContractName } from "~~/utils/scaffold-eth/contract";

/**
Expand All @@ -11,20 +12,27 @@ import { Contract, ContractName } from "~~/utils/scaffold-eth/contract";
* @param config - The config settings for the hook
* @param config.contractName - deployed contract name
* @param config.walletClient - optional walletClient from wagmi useWalletClient hook can be passed for doing write transactions
* @param config.chainId - optional chainId that is configured with the scaffold project to make use for multi-chain interactions.
*/
export const useScaffoldContract = <
TContractName extends ContractName,
TWalletClient extends Exclude<GetWalletClientReturnType, null> | undefined,
>({
contractName,
walletClient,
chainId,
}: {
rin-st marked this conversation as resolved.
Show resolved Hide resolved
contractName: TContractName;
walletClient?: TWalletClient | null;
chainId?: AllowedChainIds;
}) => {
const { data: deployedContractData, isLoading: deployedContractLoading } = useDeployedContractInfo(contractName);
const { targetNetwork } = useTargetNetwork();
const publicClient = usePublicClient({ chainId: targetNetwork.id });
const selectedNetwork = useSelectedNetwork(chainId);
const { data: deployedContractData, isLoading: deployedContractLoading } = useDeployedContractInfo({
contractName,
chainId: selectedNetwork?.id as AllowedChainIds,
});

const publicClient = usePublicClient({ chainId: selectedNetwork?.id });

let contract = undefined;
if (deployedContractData && publicClient) {
Expand Down
19 changes: 13 additions & 6 deletions packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useEffect, useState } from "react";
import { useTargetNetwork } from "./useTargetNetwork";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Abi, AbiEvent, ExtractAbiEventNames } from "abitype";
import { BlockNumber, GetLogsParameters } from "viem";
import { Config, UsePublicClientReturnType, useBlockNumber, usePublicClient } from "wagmi";
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
import { useDeployedContractInfo } from "~~/hooks/scaffold-eth";
import { AllowedChainIds } from "~~/utils/scaffold-eth";
import { replacer } from "~~/utils/scaffold-eth/common";
import {
ContractAbi,
Expand Down Expand Up @@ -57,6 +58,7 @@ const getEvents = async (
* @param config.contractName - deployed contract name
* @param config.eventName - name of the event to listen for
* @param config.fromBlock - the block number to start reading events from
* @param config.chainId - optional chainId that is configured with the scaffold project to make use for multi-chain interactions.
* @param config.filters - filters to be applied to the event (parameterName: value)
* @param config.blockData - if set to true it will return the block data for each event (default: false)
* @param config.transactionData - if set to true it will return the transaction data for each event (default: false)
Expand All @@ -74,22 +76,27 @@ export const useScaffoldEventHistory = <
contractName,
eventName,
fromBlock,
chainId,
filters,
blockData,
transactionData,
receiptData,
watch,
enabled = true,
}: UseScaffoldEventHistoryConfig<TContractName, TEventName, TBlockData, TTransactionData, TReceiptData>) => {
const { targetNetwork } = useTargetNetwork();
const selectedNetwork = useSelectedNetwork(chainId);

const publicClient = usePublicClient({
chainId: targetNetwork.id,
chainId: selectedNetwork.id,
});
const [isFirstRender, setIsFirstRender] = useState(true);

const { data: blockNumber } = useBlockNumber({ watch: watch, chainId: targetNetwork.id });
const { data: blockNumber } = useBlockNumber({ watch: watch, chainId: selectedNetwork.id });

const { data: deployedContractData } = useDeployedContractInfo(contractName);
const { data: deployedContractData } = useDeployedContractInfo({
contractName,
chainId: selectedNetwork.id as AllowedChainIds,
});

const event =
deployedContractData &&
Expand All @@ -105,7 +112,7 @@ export const useScaffoldEventHistory = <
address: deployedContractData?.address,
eventName,
fromBlock: fromBlock.toString(),
chainId: targetNetwork.id,
chainId: selectedNetwork.id,
filters: JSON.stringify(filters, replacer),
},
],
Expand Down
17 changes: 12 additions & 5 deletions packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useEffect } from "react";
import { useTargetNetwork } from "./useTargetNetwork";
import { QueryObserverResult, RefetchOptions, useQueryClient } from "@tanstack/react-query";
import type { ExtractAbiFunctionNames } from "abitype";
import { ReadContractErrorType } from "viem";
import { useBlockNumber, useReadContract } from "wagmi";
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
import { useDeployedContractInfo } from "~~/hooks/scaffold-eth";
import { AllowedChainIds } from "~~/utils/scaffold-eth";
import {
AbiFunctionReturnType,
ContractAbi,
Expand All @@ -19,6 +20,7 @@ import {
* @param config.contractName - deployed contract name
* @param config.functionName - name of the function to be called
* @param config.args - args to be passed to the function call
* @param config.chainId - optional chainId that is configured with the scaffold project to make use for multi-chain interactions.
*/
export const useScaffoldReadContract = <
TContractName extends ContractName,
Expand All @@ -27,16 +29,21 @@ export const useScaffoldReadContract = <
contractName,
functionName,
args,
chainId,
...readConfig
}: UseScaffoldReadConfig<TContractName, TFunctionName>) => {
const { data: deployedContract } = useDeployedContractInfo(contractName);
const { targetNetwork } = useTargetNetwork();
const selectedNetwork = useSelectedNetwork(chainId);
const { data: deployedContract } = useDeployedContractInfo({
contractName,
chainId: selectedNetwork.id as AllowedChainIds,
});

const { query: queryOptions, watch, ...readContractConfig } = readConfig;
// set watch to true by default
const defaultWatch = watch ?? true;

const readContractHookRes = useReadContract({
chainId: targetNetwork.id,
chainId: selectedNetwork.id,
functionName,
address: deployedContract?.address,
abi: deployedContract?.abi,
Expand All @@ -56,7 +63,7 @@ export const useScaffoldReadContract = <
const queryClient = useQueryClient();
const { data: blockNumber } = useBlockNumber({
watch: defaultWatch,
chainId: targetNetwork.id,
chainId: selectedNetwork.id,
query: {
enabled: defaultWatch,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useTargetNetwork } from "./useTargetNetwork";
import { Abi, ExtractAbiEventNames } from "abitype";
import { Log } from "viem";
import { useWatchContractEvent } from "wagmi";
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
import { addIndexedArgsToEvent, useDeployedContractInfo } from "~~/hooks/scaffold-eth";
import { AllowedChainIds } from "~~/utils/scaffold-eth";
import { ContractAbi, ContractName, UseScaffoldEventConfig } from "~~/utils/scaffold-eth/contract";

/**
Expand All @@ -11,6 +12,7 @@ import { ContractAbi, ContractName, UseScaffoldEventConfig } from "~~/utils/scaf
* @param config - The config settings
* @param config.contractName - deployed contract name
* @param config.eventName - name of the event to listen for
* @param config.chainId - optional chainId that is configured with the scaffold project to make use for multi-chain interactions.
* @param config.onLogs - the callback that receives events.
*/
export const useScaffoldWatchContractEvent = <
Expand All @@ -19,18 +21,22 @@ export const useScaffoldWatchContractEvent = <
>({
contractName,
eventName,
chainId,
onLogs,
}: UseScaffoldEventConfig<TContractName, TEventName>) => {
const { data: deployedContractData } = useDeployedContractInfo(contractName);
const { targetNetwork } = useTargetNetwork();
const selectedNetwork = useSelectedNetwork(chainId);
const { data: deployedContractData } = useDeployedContractInfo({
contractName,
chainId: selectedNetwork.id as AllowedChainIds,
});

const addIndexedArgsToLogs = (logs: Log[]) => logs.map(addIndexedArgsToEvent);
const listenerWithIndexedArgs = (logs: Log[]) => onLogs(addIndexedArgsToLogs(logs) as Parameters<typeof onLogs>[0]);

return useWatchContractEvent({
address: deployedContractData?.address,
abi: deployedContractData?.abi as Abi,
chainId: targetNetwork.id,
chainId: selectedNetwork.id,
onLogs: listenerWithIndexedArgs,
eventName,
});
Expand Down
41 changes: 25 additions & 16 deletions packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
import { useState } from "react";
import { useTargetNetwork } from "./useTargetNetwork";
import { MutateOptions } from "@tanstack/react-query";
import { Abi, ExtractAbiFunctionNames } from "abitype";
import { Config, UseWriteContractParameters, useAccount, useWriteContract } from "wagmi";
import { Config, useAccount, useWriteContract } from "wagmi";
import { WriteContractErrorType, WriteContractReturnType } from "wagmi/actions";
import { WriteContractVariables } from "wagmi/query";
import { useSelectedNetwork } from "~~/hooks/scaffold-eth";
import { useDeployedContractInfo, useTransactor } from "~~/hooks/scaffold-eth";
import { notification } from "~~/utils/scaffold-eth";
import { AllowedChainIds, notification } from "~~/utils/scaffold-eth";
import {
ContractAbi,
ContractName,
ScaffoldWriteContractOptions,
ScaffoldWriteContractVariables,
UseScaffoldWriteConfig,
} from "~~/utils/scaffold-eth/contract";

/**
* Wrapper around wagmi's useWriteContract hook which automatically loads (by name) the contract ABI and address from
* the contracts present in deployedContracts.ts & externalContracts.ts corresponding to targetNetworks configured in scaffold.config.ts
* @param contractName - name of the contract to be written to
* @param config.chainId - optional chainId that is configured with the scaffold project to make use for multi-chain interactions.
* @param writeContractParams - wagmi's useWriteContract parameters
*/
export const useScaffoldWriteContract = <TContractName extends ContractName>(
contractName: TContractName,
writeContractParams?: UseWriteContractParameters,
) => {
const { chain } = useAccount();
export const useScaffoldWriteContract = <TContractName extends ContractName>({
contractName,
chainId,
writeContractParams,
}: UseScaffoldWriteConfig<TContractName>) => {
const { chain: accountChain } = useAccount();
const writeTx = useTransactor();
const [isMining, setIsMining] = useState(false);
const { targetNetwork } = useTargetNetwork();

const wagmiContractWrite = useWriteContract(writeContractParams);

const { data: deployedContractData } = useDeployedContractInfo(contractName);
const selectedNetwork = useSelectedNetwork(chainId);

const { data: deployedContractData } = useDeployedContractInfo({
contractName,
chainId: selectedNetwork.id as AllowedChainIds,
});

const sendContractWriteAsyncTx = async <
TFunctionName extends ExtractAbiFunctionNames<ContractAbi<TContractName>, "nonpayable" | "payable">,
Expand All @@ -44,12 +51,13 @@ export const useScaffoldWriteContract = <TContractName extends ContractName>(
return;
}

if (!chain?.id) {
if (!accountChain?.id) {
notification.error("Please connect your wallet");
return;
}
if (chain?.id !== targetNetwork.id) {
notification.error("You are on the wrong network");

if (accountChain?.id !== selectedNetwork.id) {
notification.error("Your wallet is connected to the wrong network");
return;
}

Expand Down Expand Up @@ -93,12 +101,13 @@ export const useScaffoldWriteContract = <TContractName extends ContractName>(
notification.error("Target Contract is not deployed, did you forget to run `yarn deploy`?");
return;
}
if (!chain?.id) {
if (!accountChain?.id) {
notification.error("Please connect your wallet");
return;
}
if (chain?.id !== targetNetwork.id) {
notification.error("You are on the wrong network");

if (accountChain?.id !== selectedNetwork.id) {
notification.error("Your wallet is connected to the wrong network");
return;
}

Expand Down
8 changes: 8 additions & 0 deletions packages/nextjs/hooks/scaffold-eth/useSelectedNetwork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scaffoldConfig from "~~/scaffold.config";
import { useGlobalState } from "~~/services/store/store";
import { AllowedChainIds } from "~~/utils/scaffold-eth";

export function useSelectedNetwork(chainId?: AllowedChainIds) {
const targetNetwork = useGlobalState(({ targetNetwork }) => targetNetwork);
return scaffoldConfig.targetNetworks.find(targetNetwork => targetNetwork.id === chainId) ?? targetNetwork;
}
technophile-04 marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading