Skip to content

Commit

Permalink
Generate empty hardhat_contract.ts file on postInstall (#260)
Browse files Browse the repository at this point in the history
Co-authored-by: Samuel <[email protected]>
  • Loading branch information
carletex and sverps authored Mar 27, 2023
1 parent 61b837e commit 5038495
Show file tree
Hide file tree
Showing 13 changed files with 84 additions and 51 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"next:lint": "yarn workspace @se-2/nextjs lint",
"next:format": "yarn workspace @se-2/nextjs format",
"next:check-types": "yarn workspace @se-2/nextjs check-types",
"postinstall": "husky install",
"postinstall": "husky install && node packages/nextjs/scripts/generateEmptyContractFile.mjs",
"precommit": "lint-staged",
"vercel": "yarn workspace @se-2/nextjs vercel",
"vercel:yolo": "yarn workspace @se-2/nextjs vercel:yolo"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
getContractWriteMethods,
} from "~~/components/scaffold-eth";
import { useDeployedContractInfo, useNetworkColor } from "~~/hooks/scaffold-eth";
import { ContractName } from "~~/hooks/scaffold-eth/contract.types";
import { getTargetNetwork } from "~~/utils/scaffold-eth";
import { ContractName } from "~~/utils/scaffold-eth/contract";

type ContractUIProps = {
contractName: ContractName;
Expand Down
4 changes: 2 additions & 2 deletions packages/nextjs/components/scaffold-eth/Faucet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { hardhat, localhost } from "wagmi/chains";
import { BanknotesIcon } from "@heroicons/react/24/outline";
import { Address, AddressInput, Balance, EtherInput, getParsedEthersError } from "~~/components/scaffold-eth";
import { useTransactor } from "~~/hooks/scaffold-eth";
import scaffoldConfig from "~~/scaffold.config";
import { getLocalProvider, notification } from "~~/utils/scaffold-eth";
import { contracts } from "~~/utils/scaffold-eth/contract";

// Account index to use from generated hardhat accounts.
const FAUCET_ACCOUNT_INDEX = 0;
Expand All @@ -28,7 +28,7 @@ export const Faucet = () => {
useEffect(() => {
const getFaucetAddress = async () => {
try {
if (provider && scaffoldConfig.contracts) {
if (provider && Object.keys(contracts).length) {
const accounts = await provider.listAccounts();
setFaucetAddress(accounts[FAUCET_ACCOUNT_INDEX]);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/nextjs/hooks/scaffold-eth/useAutoConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { hardhat } from "wagmi/chains";
import scaffoldConfig from "~~/scaffold.config";
import { burnerWalletId, defaultBurnerChainId } from "~~/services/web3/wagmi-burner/BurnerConnector";
import { getTargetNetwork } from "~~/utils/scaffold-eth";
import { contracts } from "~~/utils/scaffold-eth/contract";

const walletIdStorageKey = "scaffoldEth2.wallet";

Expand Down Expand Up @@ -64,7 +65,7 @@ export const useAutoConnect = (): void => {
}, [accountState.isConnected, accountState.connector?.name]);

useEffectOnce(() => {
if (!scaffoldConfig.contracts) {
if (!Object.keys(contracts).length) {
return;
}

Expand Down
8 changes: 4 additions & 4 deletions packages/nextjs/hooks/scaffold-eth/useDeployedContractInfo.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { useEffect, useState } from "react";
import { Contract, ContractCodeStatus, ContractName, GenericContractsDeclaration } from "./contract.types";
import { useIsMounted } from "usehooks-ts";
import { useProvider } from "wagmi";
import scaffoldConfig from "~~/scaffold.config";
import { Contract, ContractCodeStatus, ContractName, contracts } from "~~/utils/scaffold-eth/contract";

/**
* Gets a deployed contract from `yarn deploy` generated files.
* @param contractName - name of deployed contract
*/
export const useDeployedContractInfo = <TContractName extends ContractName>(contractName: TContractName) => {
const isMounted = useIsMounted();
const deployedContract = (scaffoldConfig.contracts as unknown as GenericContractsDeclaration)?.[
scaffoldConfig.targetNetwork.id
]?.[0]?.contracts?.[contractName as ContractName] as Contract<TContractName>;
const deployedContract = contracts?.[scaffoldConfig.targetNetwork.id]?.[0]?.contracts?.[
contractName as ContractName
] as Contract<TContractName>;
const [status, setStatus] = useState<ContractCodeStatus>(ContractCodeStatus.LOADING);
const provider = useProvider({ chainId: scaffoldConfig.targetNetwork.id });

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { AbiFunctionReturnType, ContractAbi, ContractName, UseScaffoldReadConfig } from "./contract.types";
import type { ExtractAbiFunctionNames } from "abitype";
import { useContractRead } from "wagmi";
import { useDeployedContractInfo } from "~~/hooks/scaffold-eth";
import { getTargetNetwork } from "~~/utils/scaffold-eth";
import {
AbiFunctionReturnType,
ContractAbi,
ContractName,
UseScaffoldReadConfig,
} from "~~/utils/scaffold-eth/contract";

/**
* @dev wrapper for wagmi's useContractRead hook which loads in deployed contract contract abi, address automatically
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useState } from "react";
import { ContractAbi, ContractName, UseScaffoldWriteConfig } from "./contract.types";
import { Abi, ExtractAbiFunctionNames } from "abitype";
import { utils } from "ethers";
import { useContractWrite, useNetwork } from "wagmi";
import { getParsedEthersError } from "~~/components/scaffold-eth";
import { useDeployedContractInfo, useTransactor } from "~~/hooks/scaffold-eth";
import { getTargetNetwork, notification } from "~~/utils/scaffold-eth";
import { ContractAbi, ContractName, UseScaffoldWriteConfig } from "~~/utils/scaffold-eth/contract";

/**
* @dev wrapper for wagmi's useContractWrite hook(with config prepared by usePrepareContractWrite hook) which loads in deployed contract abi and address automatically
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { AbiEventArgs, ContractAbi, ContractName } from "./contract.types";
import { Abi, ExtractAbiEventNames } from "abitype";
import { useContractEvent } from "wagmi";
import { useDeployedContractInfo } from "~~/hooks/scaffold-eth";
import { getTargetNetwork } from "~~/utils/scaffold-eth";
import { AbiEventArgs, ContractAbi, ContractName, UseScaffoldEventConfig } from "~~/utils/scaffold-eth/contract";

/**
* @dev wrapper for wagmi's useContractEvent
Expand All @@ -21,12 +21,7 @@ export const useScaffoldEventSubscriber = <
eventName,
listener,
once,
}: {
contractName: TContractName;
eventName: TEventName;
listener: (...args: TEventInputs) => void;
once?: boolean;
}) => {
}: UseScaffoldEventConfig<TContractName, TEventName, TEventInputs>) => {
const { data: deployedContractData } = useDeployedContractInfo(contractName);

return useContractEvent({
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/pages/debug.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from "react";
import type { NextPage } from "next";
import { ContractUI } from "~~/components/scaffold-eth";
import { ContractName } from "~~/hooks/scaffold-eth/contract.types";
import { ContractName } from "~~/utils/scaffold-eth/contract";
import { getContractNames } from "~~/utils/scaffold-eth/contractNames";

const Debug: NextPage = () => {
Expand Down
19 changes: 0 additions & 19 deletions packages/nextjs/scaffold.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import { Abi } from "abitype";
import * as chains from "wagmi/chains";
// If you don't use custom contracts for this SE-2 app:
// - Remove the import line below
// - Set "contracts: null" in the scaffoldConfig below
import contracts from "~~/generated/hardhat_contracts";

export type ScaffoldConfig = {
targetNetwork: chains.Chain;
Expand All @@ -14,18 +9,6 @@ export type ScaffoldConfig = {
onlyLocal: boolean;
};
walletAutoConnect: boolean;
contracts: null | {
[key: number]: readonly {
name: string;
chainId: string;
contracts: {
[key: string]: {
address: string;
abi: Abi;
};
};
}[];
};
};

const scaffoldConfig = {
Expand Down Expand Up @@ -56,8 +39,6 @@ const scaffoldConfig = {
* 2. If user is not connected to any wallet: On reload, connect to burner wallet if burnerWallet.enabled is true && burnerWallet.onlyLocal is false
*/
walletAutoConnect: true,
// This is the contract data that was generated by "yarn deploy", or set it to null if no local contracts are used
contracts,
} satisfies ScaffoldConfig;

export default scaffoldConfig;
25 changes: 25 additions & 0 deletions packages/nextjs/scripts/generateEmptyContractFile.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* This script creates the 'generated/hardhat_contracts.ts' file
* if the file doesn't exist already.
*
* This way we avoid import errors when running the Next app.
*/
import fs from "fs";
import path from "path";

const __dirname = path.dirname(new URL(import.meta.url).pathname);
const nextJsDir = path.resolve(__dirname, "..");

const contractsFilePath = path.join(nextJsDir, "generated", "hardhat_contracts.ts");
const content = "export default {} as const;\n";

if (!fs.existsSync(contractsFilePath)) {
// Create the 'generated' directory if it doesn't exist
const generatedPath = path.join(nextJsDir, "generated");
if (!fs.existsSync(generatedPath)) {
fs.mkdirSync(generatedPath);
}

// Create the 'hardhat_contracts.ts'
fs.writeFileSync(contractsFilePath, content);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import { Abi, AbiParametersToPrimitiveTypes, ExtractAbiEvent, ExtractAbiEventNames, ExtractAbiFunction } from "abitype";
import type { ExtractAbiFunctionNames } from "abitype";
import { UseContractReadConfig, UseContractWriteConfig } from "wagmi";
import scaffoldConfig, { ScaffoldConfig } from "~~/scaffold.config";

export type GenericContractsDeclaration = Exclude<ScaffoldConfig["contracts"], null>;
import { UseContractEventConfig, UseContractReadConfig, UseContractWriteConfig } from "wagmi";
import contractsData from "~~/generated/hardhat_contracts";
import scaffoldConfig from "~~/scaffold.config";

type ContractsData =
| Record<string, never>
| {
[key: number]: readonly {
name: string;
chainId: string;
contracts: {
[key: string]: {
address: string;
abi: Abi;
};
};
}[];
};
export type GenericContractsDeclaration = Exclude<ContractsData, Record<string, never>>;

const contracts = scaffoldConfig.contracts;
export const contracts = contractsData as GenericContractsDeclaration;

type IsContractsFileMissing<TYes, TNo> = typeof contracts extends null ? TYes : TNo;
type ContractsDeclaration = IsContractsFileMissing<GenericContractsDeclaration, typeof contracts>;
type IsContractsFileMissing<TYes, TNo> = typeof contractsData extends Record<string, never> ? TYes : TNo;
type ContractsDeclaration = IsContractsFileMissing<GenericContractsDeclaration, typeof contractsData>;

export type Chain = keyof ContractsDeclaration;

Expand Down Expand Up @@ -139,3 +154,18 @@ export type UseScaffoldWriteConfig<
} & UseScaffoldArgsParam<TContractName, WriteAbiStateMutability, TFunctionName> &
RestConfigParam<WriteAbiStateMutability>
>;

export type UseScaffoldEventConfig<
TContractName extends ContractName,
TEventName extends ExtractAbiEventNames<ContractAbi<TContractName>>,
TEventInputs extends AbiEventArgs<ContractAbi<TContractName>, TEventName> & any[],
> = {
contractName: TContractName;
} & IsContractsFileMissing<
UseContractEventConfig,
{
eventName: TEventName;
listener: (...args: TEventInputs) => void;
once?: boolean;
}
>;
8 changes: 2 additions & 6 deletions packages/nextjs/utils/scaffold-eth/contractNames.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { ContractName, GenericContractsDeclaration } from "~~/hooks/scaffold-eth/contract.types";
import scaffoldConfig from "~~/scaffold.config";
import { ContractName, contracts } from "~~/utils/scaffold-eth/contract";

export function getContractNames() {
if (!scaffoldConfig.contracts) {
return [];
}
const contractsData = (scaffoldConfig.contracts as GenericContractsDeclaration)[scaffoldConfig.targetNetwork.id]?.[0]
?.contracts;
const contractsData = contracts?.[scaffoldConfig.targetNetwork.id]?.[0]?.contracts;
return contractsData ? (Object.keys(contractsData) as ContractName[]) : [];
}

0 comments on commit 5038495

Please sign in to comment.