From 9c967d971a7340850d580b4679b79ef1b4e68181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Thu, 5 Oct 2023 06:52:01 +0200 Subject: [PATCH] Update CLI with latest main (#555) Co-authored-by: Rinat Co-authored-by: Shiv Bhonde | shivbhonde.eth Co-authored-by: CJ <53488449+0xChijoke@users.noreply.github.com> Co-authored-by: Zak G Co-authored-by: owlwilderness.eth <98717833+OwlWilderness@users.noreply.github.com> Co-authored-by: port <108868128+portdeveloper@users.noreply.github.com> Co-authored-by: Pierre Bertet Co-authored-by: isabellewei Co-authored-by: Filip Harald --- .changeset/angry-tips-speak.md | 41 +++++++ .../packages/nextjs/components/Footer.tsx | 22 ++-- .../components/assets/BuidlGuidlLogo.tsx | 18 +++ .../components/{ => assets}/Spinner.tsx | 0 .../blockexplorer/TransactionsTable.tsx | 99 +++++++--------- .../example-ui/ContractInteraction.tsx | 3 +- .../components/scaffold-eth/Address.tsx | 25 +--- .../components/scaffold-eth/BlockieAvatar.tsx | 21 ++-- .../scaffold-eth/Contract/ContractUI.tsx | 2 +- .../scaffold-eth/Input/AddressInput.tsx | 8 +- .../RainbowKitCustomConnectButton.tsx | 2 +- .../hooks/scaffold-eth/useAutoConnect.ts | 11 +- .../hooks/scaffold-eth/useFetchBlocks.ts | 86 ++++++++------ .../scaffold-eth/useScaffoldContractWrite.ts | 5 +- .../scaffold-eth/useScaffoldEventHistory.ts | 28 ++++- .../useScaffoldEventSubscriber.ts | 8 +- templates/base/packages/nextjs/package.json | 11 +- .../pages/blockexplorer/address/[address].tsx | 4 +- .../nextjs/pages/blockexplorer/index.tsx | 4 +- .../base/packages/nextjs/public/manifest.json | 5 + .../nextjs/services/web3/wagmiConnectors.tsx | 2 + .../nextjs/utils/scaffold-eth/block.ts | 1 - .../nextjs/utils/scaffold-eth/contract.ts | 109 ++++++++++++++---- .../nextjs/utils/scaffold-eth/decodeTxData.ts | 2 +- .../scaffold-eth/fetchPriceFromUniswap.ts | 6 +- .../nextjs/utils/scaffold-eth/networks.ts | 3 + .../utils/scaffold-eth/notification.tsx | 2 +- .../foundry/packages/foundry/package.json | 2 +- .../packages/hardhat/hardhat.config.ts | 12 ++ .../hardhat/packages/hardhat/package.json | 2 +- 30 files changed, 359 insertions(+), 185 deletions(-) create mode 100644 .changeset/angry-tips-speak.md create mode 100644 templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx rename templates/base/packages/nextjs/components/{ => assets}/Spinner.tsx (100%) create mode 100644 templates/base/packages/nextjs/public/manifest.json diff --git a/.changeset/angry-tips-speak.md b/.changeset/angry-tips-speak.md new file mode 100644 index 000000000..008bb73b6 --- /dev/null +++ b/.changeset/angry-tips-speak.md @@ -0,0 +1,41 @@ +--- +"create-eth": patch +--- + +1. Fix RainbowKitCustomConnectButton dropdown styles (#500) + +2. chore: footer missalignment & home page on small screens (#502) + +3. Update and typescript version (#499) + +4. fix: header links wrapping icons and text (#510) + +5. Fix typos in useScaffoldContractWrite logs (#512) + +6. fix: spelling in test file name (#522) + +7. Add polygonZkEvm and polygonZkEvmTestnet (#309) + +8. fix: eth price showing 0 on sepolia network (#532) + +9. use websockets client in useFetchBlocks hooks (#529) + +10. Move from react-blockies to blo (#538) + +11. add Prettify type helper locally (#541) + +12. update hardhat version (#546) + +13. Add indexed args to events (#540) + +14. add out of box compatibility with SAFE{Wallet} (#346) + +15. remove parseEther from useScaffoldContractWrite (#548) + +16. Add Scroll Sepolia testnet (#547) + +17. use BuidlGuidl logo on footer (#551) + +18. add types to even data in useScaffoldEventHistory (#553) + +19. foundry: add chain id 31337 while forking chain (#531) diff --git a/templates/base/packages/nextjs/components/Footer.tsx b/templates/base/packages/nextjs/components/Footer.tsx index e0f419446..8e8581060 100644 --- a/templates/base/packages/nextjs/components/Footer.tsx +++ b/templates/base/packages/nextjs/components/Footer.tsx @@ -2,6 +2,7 @@ import { hardhat } from "wagmi/chains"; import { CurrencyDollarIcon } from "@heroicons/react/24/outline"; import { HeartIcon } from "@heroicons/react/24/outline"; import { SwitchTheme } from "~~/components/SwitchTheme"; +import { BuidlGuidlLogo } from "~~/components/assets/BuidlGuidlLogo"; import { Faucet } from "~~/components/scaffold-eth"; import { useGlobalState } from "~~/services/store/store"; import { getTargetNetwork } from "~~/utils/scaffold-eth"; @@ -42,18 +43,19 @@ export const Footer = () => { · -
+

- Built with at 🏰{" "} - - BuidlGuidl - + Built with at

+ + + BuidlGuidl +
·
diff --git a/templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx b/templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx new file mode 100644 index 000000000..af46b02f1 --- /dev/null +++ b/templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx @@ -0,0 +1,18 @@ +export const BuidlGuidlLogo = ({ className }: { className: string }) => { + return ( + + + + ); +}; diff --git a/templates/base/packages/nextjs/components/Spinner.tsx b/templates/base/packages/nextjs/components/assets/Spinner.tsx similarity index 100% rename from templates/base/packages/nextjs/components/Spinner.tsx rename to templates/base/packages/nextjs/components/assets/Spinner.tsx diff --git a/templates/base/packages/nextjs/components/blockexplorer/TransactionsTable.tsx b/templates/base/packages/nextjs/components/blockexplorer/TransactionsTable.tsx index d447aaea6..5f9d4611c 100644 --- a/templates/base/packages/nextjs/components/blockexplorer/TransactionsTable.tsx +++ b/templates/base/packages/nextjs/components/blockexplorer/TransactionsTable.tsx @@ -4,7 +4,7 @@ import { Address } from "~~/components/scaffold-eth"; import { TransactionWithFunction, getTargetNetwork } from "~~/utils/scaffold-eth"; import { TransactionsTableProps } from "~~/utils/scaffold-eth/"; -export const TransactionsTable = ({ blocks, transactionReceipts, isLoading }: TransactionsTableProps) => { +export const TransactionsTable = ({ blocks, transactionReceipts }: TransactionsTableProps) => { const targetNetwork = getTargetNetwork(); return ( @@ -22,64 +22,47 @@ export const TransactionsTable = ({ blocks, transactionReceipts, isLoading }: Tr Value ({targetNetwork.nativeCurrency.symbol}) - {isLoading ? ( - - {[...Array(20)].map((_, rowIndex) => ( - - {[...Array(7)].map((_, colIndex) => ( - -
- - ))} - - ))} - - ) : ( - - {blocks.map(block => - (block.transactions as TransactionWithFunction[]).map(tx => { - const receipt = transactionReceipts[tx.hash]; - const timeMined = new Date(Number(block.timestamp) * 1000).toLocaleString(); - const functionCalled = tx.input.substring(0, 10); + + {blocks.map(block => + (block.transactions as TransactionWithFunction[]).map(tx => { + const receipt = transactionReceipts[tx.hash]; + const timeMined = new Date(Number(block.timestamp) * 1000).toLocaleString(); + const functionCalled = tx.input.substring(0, 10); - return ( - - - - - - {tx.functionName === "0x" ? "" : {tx.functionName}} - {functionCalled !== "0x" && ( - {functionCalled} - )} - - {block.number?.toString()} - {timeMined} - -
- - - {!receipt?.contractAddress ? ( - tx.to &&
- ) : ( -
-
- (Contract Creation) -
- )} - - - {formatEther(tx.value)} {targetNetwork.nativeCurrency.symbol} - - - ); - }), - )} - - )} + return ( + + + + + + {tx.functionName === "0x" ? "" : {tx.functionName}} + {functionCalled !== "0x" && ( + {functionCalled} + )} + + {block.number?.toString()} + {timeMined} + +
+ + + {!receipt?.contractAddress ? ( + tx.to &&
+ ) : ( +
+
+ (Contract Creation) +
+ )} + + + {formatEther(tx.value)} {targetNetwork.nativeCurrency.symbol} + + + ); + }), + )} +
diff --git a/templates/base/packages/nextjs/components/example-ui/ContractInteraction.tsx b/templates/base/packages/nextjs/components/example-ui/ContractInteraction.tsx index 7c0d1c4fb..d541ecd69 100644 --- a/templates/base/packages/nextjs/components/example-ui/ContractInteraction.tsx +++ b/templates/base/packages/nextjs/components/example-ui/ContractInteraction.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { CopyIcon } from "./assets/CopyIcon"; import { DiamondIcon } from "./assets/DiamondIcon"; import { HareIcon } from "./assets/HareIcon"; +import { parseEther } from "viem"; import { ArrowSmallRightIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth"; @@ -13,7 +14,7 @@ export const ContractInteraction = () => { contractName: "YourContract", functionName: "setGreeting", args: [newGreeting], - value: "0.01", + value: parseEther("0.01"), onBlockConfirmation: txnReceipt => { console.log("📦 Transaction blockHash", txnReceipt.blockHash); }, diff --git a/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx b/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx index 3b0c9172d..c8af261db 100644 --- a/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx +++ b/templates/base/packages/nextjs/components/scaffold-eth/Address.tsx @@ -1,11 +1,11 @@ import { useEffect, useState } from "react"; import Link from "next/link"; -import Blockies from "react-blockies"; import { CopyToClipboard } from "react-copy-to-clipboard"; import { isAddress } from "viem"; import { useEnsAvatar, useEnsName } from "wagmi"; import { hardhat } from "wagmi/chains"; import { CheckCircleIcon, DocumentDuplicateIcon } from "@heroicons/react/24/outline"; +import { BlockieAvatar } from "~~/components/scaffold-eth"; import { getBlockExplorerAddressLink, getTargetNetwork } from "~~/utils/scaffold-eth"; type TAddressProps = { @@ -78,24 +78,11 @@ export const Address = ({ address, disableAddressLink, format, size = "base" }: return (
- {ensAvatar ? ( - // Don't want to use nextJS Image here (and adding remote patterns for the URL) - // eslint-disable-next-line - {`${address} - ) : ( - - )} +
{disableAddressLink ? ( {displayAddress} diff --git a/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx b/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx index 880a6aaad..9c1ea4d67 100644 --- a/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx +++ b/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx @@ -1,12 +1,15 @@ import { AvatarComponent } from "@rainbow-me/rainbowkit"; -import Blockies from "react-blockies"; +import { blo } from "blo"; // Custom Avatar for RainbowKit -export const BlockieAvatar: AvatarComponent = ({ address, ensImage, size }) => - ensImage ? ( - // Don't want to use nextJS Image here (and adding remote patterns for the URL) - // eslint-disable-next-line - {`${address} - ) : ( - 30 ? 10 : 3.75} /> - ); +export const BlockieAvatar: AvatarComponent = ({ address, ensImage, size }) => ( + // Don't want to use nextJS Image here (and adding remote patterns for the URL) + // eslint-disable-next-line @next/next/no-img-element + {`${address} +); diff --git a/templates/base/packages/nextjs/components/scaffold-eth/Contract/ContractUI.tsx b/templates/base/packages/nextjs/components/scaffold-eth/Contract/ContractUI.tsx index 202ad19e0..bcacf9c08 100644 --- a/templates/base/packages/nextjs/components/scaffold-eth/Contract/ContractUI.tsx +++ b/templates/base/packages/nextjs/components/scaffold-eth/Contract/ContractUI.tsx @@ -2,7 +2,7 @@ import { useReducer } from "react"; import { ContractReadMethods } from "./ContractReadMethods"; import { ContractVariables } from "./ContractVariables"; import { ContractWriteMethods } from "./ContractWriteMethods"; -import { Spinner } from "~~/components/Spinner"; +import { Spinner } from "~~/components/assets/Spinner"; import { Address, Balance } from "~~/components/scaffold-eth"; import { useDeployedContractInfo, useNetworkColor } from "~~/hooks/scaffold-eth"; import { getTargetNetwork } from "~~/utils/scaffold-eth"; diff --git a/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx b/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx index 736dd8be0..d623caba0 100644 --- a/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx +++ b/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import Blockies from "react-blockies"; +import { blo } from "blo"; import { isAddress } from "viem"; import { Address } from "viem"; import { useEnsAddress, useEnsAvatar, useEnsName } from "wagmi"; @@ -74,7 +74,11 @@ export const AddressInput = ({ value, name, placeholder, onChange, disabled }: C
) } - suffix={value && } + suffix={ + // Don't want to use nextJS Image here (and adding remote patterns for the URL) + // eslint-disable-next-line @next/next/no-img-element + value && + } /> ); }; diff --git a/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton.tsx b/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton.tsx index c41d8cf9a..803bb8c73 100644 --- a/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton.tsx +++ b/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton.tsx @@ -96,7 +96,7 @@ export const RainbowKitCustomConnectButton = () => { tabIndex={0} className="btn btn-secondary btn-sm pl-0 pr-2 shadow-md dropdown-toggle gap-0 !h-auto" > - + {account.displayName} diff --git a/templates/base/packages/nextjs/hooks/scaffold-eth/useAutoConnect.ts b/templates/base/packages/nextjs/hooks/scaffold-eth/useAutoConnect.ts index 6bf734930..bd5ea8d86 100644 --- a/templates/base/packages/nextjs/hooks/scaffold-eth/useAutoConnect.ts +++ b/templates/base/packages/nextjs/hooks/scaffold-eth/useAutoConnect.ts @@ -9,6 +9,9 @@ import { getTargetNetwork } from "~~/utils/scaffold-eth"; const SCAFFOLD_WALLET_STROAGE_KEY = "scaffoldEth2.wallet"; const WAGMI_WALLET_STORAGE_KEY = "wagmi.wallet"; +// ID of the SAFE connector instance +const SAFE_ID = "safe"; + /** * This function will get the initial wallet connector (if any), the app will connect to * @param previousWalletId @@ -19,8 +22,14 @@ const getInitialConnector = ( previousWalletId: string, connectors: Connector[], ): { connector: Connector | undefined; chainId?: number } | undefined => { - const targetNetwork = getTargetNetwork(); + // Look for the SAFE connector instance and connect to it instantly if loaded in SAFE frame + const safeConnectorInstance = connectors.find(connector => connector.id === SAFE_ID && connector.ready); + if (safeConnectorInstance) { + return { connector: safeConnectorInstance }; + } + + const targetNetwork = getTargetNetwork(); const allowBurner = scaffoldConfig.onlyLocalBurnerWallet ? targetNetwork.id === hardhat.id : true; if (!previousWalletId) { diff --git a/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts b/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts index c927d6a32..0918c65e3 100644 --- a/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts +++ b/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts @@ -1,29 +1,41 @@ import { useCallback, useEffect, useState } from "react"; -import { Block, Transaction, TransactionReceipt } from "viem"; -import { usePublicClient } from "wagmi"; +import { + Block, + Hash, + Transaction, + TransactionReceipt, + createTestClient, + publicActions, + walletActions, + webSocket, +} from "viem"; import { hardhat } from "wagmi/chains"; import { decodeTransactionData } from "~~/utils/scaffold-eth"; const BLOCKS_PER_PAGE = 20; -export const useFetchBlocks = () => { - const client = usePublicClient({ chainId: hardhat.id }); +export const testClient = createTestClient({ + chain: hardhat, + mode: "hardhat", + transport: webSocket("ws://127.0.0.1:8545"), +}) + .extend(publicActions) + .extend(walletActions); +export const useFetchBlocks = () => { const [blocks, setBlocks] = useState([]); const [transactionReceipts, setTransactionReceipts] = useState<{ [key: string]: TransactionReceipt; }>({}); const [currentPage, setCurrentPage] = useState(0); const [totalBlocks, setTotalBlocks] = useState(0n); - const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const fetchBlocks = useCallback(async () => { - setIsLoading(true); setError(null); try { - const blockNumber = await client.getBlockNumber(); + const blockNumber = await testClient.getBlockNumber(); setTotalBlocks(blockNumber); const startingBlock = blockNumber - BigInt(currentPage * BLOCKS_PER_PAGE); @@ -34,7 +46,7 @@ export const useFetchBlocks = () => { const blocksWithTransactions = blockNumbersToFetch.map(async blockNumber => { try { - return client.getBlock({ blockNumber, includeTransactions: true }); + return testClient.getBlock({ blockNumber, includeTransactions: true }); } catch (err) { setError(err instanceof Error ? err : new Error("An error occurred.")); throw err; @@ -50,7 +62,7 @@ export const useFetchBlocks = () => { fetchedBlocks.flatMap(block => block.transactions.map(async tx => { try { - const receipt = await client.getTransactionReceipt({ hash: (tx as Transaction).hash }); + const receipt = await testClient.getTransactionReceipt({ hash: (tx as Transaction).hash }); return { [(tx as Transaction).hash]: receipt }; } catch (err) { setError(err instanceof Error ? err : new Error("An error occurred.")); @@ -65,47 +77,50 @@ export const useFetchBlocks = () => { } catch (err) { setError(err instanceof Error ? err : new Error("An error occurred.")); } - setIsLoading(false); - }, [client, currentPage]); + }, [currentPage]); useEffect(() => { fetchBlocks(); }, [fetchBlocks]); useEffect(() => { - const handleNewBlock = async (newBlock: Block) => { + const handleNewBlock = async (newBlock: any) => { try { - if (!blocks.some(block => block.number === newBlock.number)) { - if (currentPage === 0) { - setBlocks(prevBlocks => [newBlock, ...prevBlocks.slice(0, BLOCKS_PER_PAGE - 1)]); - - newBlock.transactions.forEach(tx => decodeTransactionData(tx as Transaction)); - - const receipts = await Promise.all( - newBlock.transactions.map(async tx => { - try { - const receipt = await client.getTransactionReceipt({ hash: (tx as Transaction).hash }); - return { [(tx as Transaction).hash]: receipt }; - } catch (err) { - setError(err instanceof Error ? err : new Error("An error occurred.")); - throw err; - } - }), + if (currentPage === 0) { + if (newBlock.transactions.length > 0) { + const transactionsDetails = await Promise.all( + newBlock.transactions.map((txHash: string) => testClient.getTransaction({ hash: txHash as Hash })), ); - - setTransactionReceipts(prevReceipts => ({ ...prevReceipts, ...Object.assign({}, ...receipts) })); - } - if (newBlock.number) { - setTotalBlocks(newBlock.number); + newBlock.transactions = transactionsDetails; } + + newBlock.transactions.forEach((tx: Transaction) => decodeTransactionData(tx as Transaction)); + + const receipts = await Promise.all( + newBlock.transactions.map(async (tx: Transaction) => { + try { + const receipt = await testClient.getTransactionReceipt({ hash: (tx as Transaction).hash }); + return { [(tx as Transaction).hash]: receipt }; + } catch (err) { + setError(err instanceof Error ? err : new Error("An error occurred fetching receipt.")); + throw err; + } + }), + ); + + setBlocks(prevBlocks => [newBlock, ...prevBlocks.slice(0, BLOCKS_PER_PAGE - 1)]); + setTransactionReceipts(prevReceipts => ({ ...prevReceipts, ...Object.assign({}, ...receipts) })); + } + if (newBlock.number) { + setTotalBlocks(newBlock.number); } } catch (err) { setError(err instanceof Error ? err : new Error("An error occurred.")); } }; - return client.watchBlocks({ onBlock: handleNewBlock, includeTransactions: true }); - }, [blocks, client, currentPage]); + return testClient.watchBlocks({ onBlock: handleNewBlock, includeTransactions: true }); + }, [currentPage]); return { blocks, @@ -113,7 +128,6 @@ export const useFetchBlocks = () => { currentPage, totalBlocks, setCurrentPage, - isLoading, error, }; }; diff --git a/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContractWrite.ts b/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContractWrite.ts index c8207c995..773922080 100644 --- a/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContractWrite.ts +++ b/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContractWrite.ts @@ -1,6 +1,5 @@ import { useState } from "react"; import { Abi, ExtractAbiFunctionNames } from "abitype"; -import { parseEther } from "viem"; import { useContractWrite, useNetwork } from "wagmi"; import { getParsedError } from "~~/components/scaffold-eth"; import { useDeployedContractInfo, useTransactor } from "~~/hooks/scaffold-eth"; @@ -41,7 +40,7 @@ export const useScaffoldContractWrite = < abi: deployedContractData?.abi as Abi, functionName: functionName as any, args: args as unknown[], - value: value ? parseEther(value) : undefined, + value: value, ...writeConfig, }); @@ -73,7 +72,7 @@ export const useScaffoldContractWrite = < () => wagmiContractWrite.writeAsync({ args: newArgs ?? args, - value: newValue ? parseEther(newValue) : value && parseEther(value), + value: newValue ?? value, ...otherConfig, }), { onBlockConfirmation, blockConfirmations }, diff --git a/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts b/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts index bd4700eb9..d71ee1e75 100644 --- a/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts +++ b/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts @@ -4,7 +4,12 @@ import { Hash } from "viem"; import { usePublicClient } from "wagmi"; import { useDeployedContractInfo } from "~~/hooks/scaffold-eth"; import { replacer } from "~~/utils/scaffold-eth/common"; -import { ContractAbi, ContractName, UseScaffoldEventHistoryConfig } from "~~/utils/scaffold-eth/contract"; +import { + ContractAbi, + ContractName, + UseScaffoldEventHistoryConfig, + UseScaffoldEventHistoryData, +} from "~~/utils/scaffold-eth/contract"; /** * @dev reads events from a deployed contract @@ -20,6 +25,9 @@ import { ContractAbi, ContractName, UseScaffoldEventHistoryConfig } from "~~/uti export const useScaffoldEventHistory = < TContractName extends ContractName, TEventName extends ExtractAbiEventNames>, + TBlockData extends boolean = false, + TTransactionData extends boolean = false, + TReceiptData extends boolean = false, >({ contractName, eventName, @@ -28,7 +36,7 @@ export const useScaffoldEventHistory = < blockData, transactionData, receiptData, -}: UseScaffoldEventHistoryConfig) => { +}: UseScaffoldEventHistoryConfig) => { const [events, setEvents] = useState(); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(); @@ -101,8 +109,22 @@ export const useScaffoldEventHistory = < ]); return { - data: events, + data: events?.map(addIndexedArgsToEvent) as UseScaffoldEventHistoryData< + TContractName, + TEventName, + TBlockData, + TTransactionData, + TReceiptData + >, isLoading: isLoading, error: error, }; }; + +export const addIndexedArgsToEvent = (event: any) => { + if (event.args && !Array.isArray(event.args)) { + return { ...event, args: { ...event.args, ...Object.values(event.args) } }; + } + + return event; +}; diff --git a/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventSubscriber.ts b/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventSubscriber.ts index 24db4a28e..96c1dbacd 100644 --- a/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventSubscriber.ts +++ b/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventSubscriber.ts @@ -1,7 +1,7 @@ import { Abi, ExtractAbiEventNames } from "abitype"; import { Log } from "viem"; import { useContractEvent } from "wagmi"; -import { useDeployedContractInfo } from "~~/hooks/scaffold-eth"; +import { addIndexedArgsToEvent, useDeployedContractInfo } from "~~/hooks/scaffold-eth"; import { getTargetNetwork } from "~~/utils/scaffold-eth"; import { ContractAbi, ContractName, UseScaffoldEventConfig } from "~~/utils/scaffold-eth/contract"; @@ -22,11 +22,15 @@ export const useScaffoldEventSubscriber = < }: UseScaffoldEventConfig) => { const { data: deployedContractData } = useDeployedContractInfo(contractName); + const addInexedArgsToLogs = (logs: Log[]) => logs.map(addIndexedArgsToEvent); + const listenerWithIndexedArgs = (logs: Log[]) => + listener(addInexedArgsToLogs(logs) as Parameters[0]); + return useContractEvent({ address: deployedContractData?.address, abi: deployedContractData?.abi as Abi, chainId: getTargetNetwork().id, - listener: listener as (logs: Log[]) => void, + listener: listenerWithIndexedArgs, eventName, }); }; diff --git a/templates/base/packages/nextjs/package.json b/templates/base/packages/nextjs/package.json index db85bdade..7b9d9fdd6 100644 --- a/templates/base/packages/nextjs/package.json +++ b/templates/base/packages/nextjs/package.json @@ -8,7 +8,7 @@ "build": "next build", "serve": "next start", "lint": "next lint", - "format": "prettier --write . '!(node_module|.next|contracts)/**/*'", + "format": "prettier --write . '!(node_modules|.next|contracts)/**/*'", "check-types": "tsc --noEmit --incremental", "vercel": "vercel", "vercel:yolo": "vercel --build-env NEXT_PUBLIC_IGNORE_BUILD_ERROR=true" @@ -19,12 +19,12 @@ "@rainbow-me/rainbowkit": "1.0.8", "@uniswap/sdk-core": "~4.0.1", "@uniswap/v2-sdk": "~3.0.1", + "blo": "~1.0.1", "daisyui": "~3.5.1", "next": "~13.1.6", "nextjs-progressbar": "~0.0.16", "qrcode.react": "~3.1.0", "react": "~18.2.0", - "react-blockies": "~1.4.1", "react-copy-to-clipboard": "~5.1.0", "react-dom": "~18.2.0", "react-fast-marquee": "~1.3.5", @@ -37,10 +37,9 @@ }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "~4.1.1", - "@types/node": "~17.0.45", - "@types/react": "~18.0.21", - "@types/react-blockies": "~1.4.1", - "@types/react-copy-to-clipboard": "~5.0.4", + "@types/node": "^17.0.45", + "@types/react": "^18.0.21", + "@types/react-copy-to-clipboard": "^5.0.4", "@typescript-eslint/eslint-plugin": "~5.40.0", "autoprefixer": "~10.4.12", "eslint": "~8.24.0", diff --git a/templates/base/packages/nextjs/pages/blockexplorer/address/[address].tsx b/templates/base/packages/nextjs/pages/blockexplorer/address/[address].tsx index 757c037fe..6ad99e85e 100644 --- a/templates/base/packages/nextjs/pages/blockexplorer/address/[address].tsx +++ b/templates/base/packages/nextjs/pages/blockexplorer/address/[address].tsx @@ -35,7 +35,7 @@ const publicClient = createPublicClient({ const AddressPage = ({ address, contractData }: PageProps) => { const router = useRouter(); - const { blocks, transactionReceipts, currentPage, totalBlocks, setCurrentPage, isLoading } = useFetchBlocks(); + const { blocks, transactionReceipts, currentPage, totalBlocks, setCurrentPage } = useFetchBlocks(); const [activeTab, setActiveTab] = useState("transactions"); const [isContract, setIsContract] = useState(false); @@ -109,7 +109,7 @@ const AddressPage = ({ address, contractData }: PageProps) => { )} {activeTab === "transactions" && (
- + { - const { blocks, transactionReceipts, currentPage, totalBlocks, setCurrentPage, isLoading, error } = useFetchBlocks(); + const { blocks, transactionReceipts, currentPage, totalBlocks, setCurrentPage, error } = useFetchBlocks(); useEffect(() => { if (getTargetNetwork().id === hardhat.id && error) { @@ -51,7 +51,7 @@ const Blockexplorer: NextPage = () => { return (
- +
); diff --git a/templates/base/packages/nextjs/public/manifest.json b/templates/base/packages/nextjs/public/manifest.json new file mode 100644 index 000000000..bb1c96df9 --- /dev/null +++ b/templates/base/packages/nextjs/public/manifest.json @@ -0,0 +1,5 @@ +{ + "name": "Scaffold-ETH 2 DApp", + "description": "A DApp built with Scaffold-ETH", + "iconPath": "logo.svg" +} diff --git a/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx b/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx index 9985dc6f1..cee7318fd 100644 --- a/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx +++ b/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx @@ -5,6 +5,7 @@ import { ledgerWallet, metaMaskWallet, rainbowWallet, + safeWallet, walletConnectWallet, } from "@rainbow-me/rainbowkit/wallets"; import { configureChains } from "wagmi"; @@ -55,6 +56,7 @@ const wallets = [ ...(configuredNetwork.id === chains.hardhat.id || !onlyLocalBurnerWallet ? [burnerWalletConfig({ chains: [appChains.chains[0]] })] : []), + safeWallet({ ...walletsOptions, debug: false, allowedDomains: [/gnosis-safe.io$/, /app.safe.global$/] }), ]; /** diff --git a/templates/base/packages/nextjs/utils/scaffold-eth/block.ts b/templates/base/packages/nextjs/utils/scaffold-eth/block.ts index ae22f3152..4cd753fe3 100644 --- a/templates/base/packages/nextjs/utils/scaffold-eth/block.ts +++ b/templates/base/packages/nextjs/utils/scaffold-eth/block.ts @@ -14,5 +14,4 @@ interface TransactionReceipts { export interface TransactionsTableProps { blocks: Block[]; transactionReceipts: TransactionReceipts; - isLoading: boolean; } diff --git a/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts b/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts index ba9546a50..046bd559b 100644 --- a/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts +++ b/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts @@ -7,12 +7,29 @@ import { ExtractAbiFunction, } from "abitype"; import type { ExtractAbiFunctionNames } from "abitype"; -import { Address, Log, TransactionReceipt } from "viem"; -import { Prettify } from "viem/dist/types/types/utils"; +import { + Address, + Block, + GetEventArgs, + GetTransactionReceiptReturnType, + GetTransactionReturnType, + Log, + TransactionReceipt, +} from "viem"; import { UseContractEventConfig, UseContractReadConfig, UseContractWriteConfig } from "wagmi"; import contractsData from "~~/generated/deployedContracts"; import scaffoldConfig from "~~/scaffold.config"; +/** + * @description Combines members of an intersection into a readable type. + * @example + * Prettify<{ a: string } & { b: string } & { c: number, d: bigint }> + * => { a: string, b: string, c: number, d: bigint } + */ +type Prettify = { + [K in keyof T]: T[K]; +} & unknown; + export type GenericContractsDeclaration = { [key: number]: readonly { name: string; @@ -113,17 +130,6 @@ type UseScaffoldArgsParam< args?: never; }; -type ExtractStateMutability< - TContractName extends ContractName, - TFunctionName extends ExtractAbiFunctionNames, WriteAbiStateMutability>, -> = Extract< - ContractAbi[number], - { - name: TFunctionName; - stateMutability: string; - } ->["stateMutability"]; - export type UseScaffoldReadConfig< TContractName extends ContractName, TFunctionName extends ExtractAbiFunctionNames, ReadAbiStateMutability>, @@ -145,25 +151,49 @@ export type UseScaffoldWriteConfig< onBlockConfirmation?: (txnReceipt: TransactionReceipt) => void; blockConfirmations?: number; } & IsContractDeclarationMissing< - Partial & { value: `${number}` }>, - (ExtractStateMutability extends "payable" - ? { value: `${number}` } - : { value?: never }) & { + Partial, + { functionName: TFunctionName; } & UseScaffoldArgsParam & - Omit + Omit >; export type UseScaffoldEventConfig< TContractName extends ContractName, TEventName extends ExtractAbiEventNames>, + TEvent extends ExtractAbiEvent, TEventName> = ExtractAbiEvent< + ContractAbi, + TEventName + >, > = { contractName: TContractName; } & IsContractDeclarationMissing< Omit & { - listener: (logs: Prettify, "args"> & { args: Record }>[]) => void; + listener: ( + logs: Prettify< + Omit, "args" | "eventName"> & { + args: Record; + eventName: string; + } + >[], + ) => void; }, - UseContractEventConfig, TEventName> + Omit, TEventName>, "listener"> & { + listener: ( + logs: Prettify< + Omit, "args"> & { + args: AbiParametersToPrimitiveTypes & + GetEventArgs< + ContractAbi, + TEventName, + { + IndexedOnly: false; + } + >; + } + >[], + ) => void; + } >; type IndexedEventInputs< @@ -189,12 +219,45 @@ export type EventFilters< export type UseScaffoldEventHistoryConfig< TContractName extends ContractName, TEventName extends ExtractAbiEventNames>, + TBlockData extends boolean = false, + TTransactionData extends boolean = false, + TReceiptData extends boolean = false, > = { contractName: TContractName; eventName: IsContractDeclarationMissing; fromBlock: bigint; filters?: EventFilters; - blockData?: boolean; - transactionData?: boolean; - receiptData?: boolean; + blockData?: TBlockData; + transactionData?: TTransactionData; + receiptData?: TReceiptData; }; + +export type UseScaffoldEventHistoryData< + TContractName extends ContractName, + TEventName extends ExtractAbiEventNames>, + TBlockData extends boolean = false, + TTransactionData extends boolean = false, + TReceiptData extends boolean = false, + TEvent extends ExtractAbiEvent, TEventName> = ExtractAbiEvent< + ContractAbi, + TEventName + >, +> = + | IsContractDeclarationMissing< + any[], + { + log: Log; + args: AbiParametersToPrimitiveTypes & + GetEventArgs< + ContractAbi, + TEventName, + { + IndexedOnly: false; + } + >; + block: TBlockData extends true ? Block : null; + receipt: TReceiptData extends true ? GetTransactionReturnType : null; + transaction: TTransactionData extends true ? GetTransactionReceiptReturnType : null; + }[] + > + | undefined; diff --git a/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts b/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts index a32ceabf5..423fe50f3 100644 --- a/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts +++ b/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts @@ -17,7 +17,7 @@ const interfaces = chainMetaData : {}; export const decodeTransactionData = (tx: TransactionWithFunction) => { - if (tx.input.length >= 10) { + if (tx.input.length >= 10 && !tx.input.startsWith("0x60e06040")) { for (const [, contractAbi] of Object.entries(interfaces)) { try { const { functionName, args } = decodeFunctionData({ diff --git a/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts b/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts index 51c8136c7..312b07b6d 100644 --- a/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts +++ b/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts @@ -18,7 +18,11 @@ const ABI = parseAbi([ export const fetchPriceFromUniswap = async (): Promise => { const configuredNetwork = getTargetNetwork(); - if (configuredNetwork.nativeCurrency.symbol !== "ETH" && !configuredNetwork.nativeCurrencyTokenAddress) { + if ( + configuredNetwork.nativeCurrency.symbol !== "ETH" && + configuredNetwork.nativeCurrency.symbol !== "SEP" && + !configuredNetwork.nativeCurrencyTokenAddress + ) { return 0; } try { diff --git a/templates/base/packages/nextjs/utils/scaffold-eth/networks.ts b/templates/base/packages/nextjs/utils/scaffold-eth/networks.ts index f9e576e69..ab938166e 100644 --- a/templates/base/packages/nextjs/utils/scaffold-eth/networks.ts +++ b/templates/base/packages/nextjs/utils/scaffold-eth/networks.ts @@ -51,6 +51,9 @@ export const NETWORKS_EXTRA_DATA: Record = { [chains.fantomTestnet.id]: { color: "#1969ff", }, + [chains.scrollSepolia.id]: { + color: '#fbebd4' + } }; /** diff --git a/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx b/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx index c80722bb7..bc454d426 100644 --- a/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx +++ b/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx @@ -7,7 +7,7 @@ import { ExclamationTriangleIcon, InformationCircleIcon, } from "@heroicons/react/24/solid"; -import { Spinner } from "~~/components/Spinner"; +import { Spinner } from "~~/components/assets/Spinner"; type TPositions = "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"; diff --git a/templates/extensions/foundry/packages/foundry/package.json b/templates/extensions/foundry/packages/foundry/package.json index aae21f989..7dcd15133 100644 --- a/templates/extensions/foundry/packages/foundry/package.json +++ b/templates/extensions/foundry/packages/foundry/package.json @@ -4,7 +4,7 @@ "scripts": { "account": "node script/ListAccount.js", "chain": "anvil --config-out localhost.json", - "fork": "anvil --fork-url ${0:-mainnet} --config-out localhost.json", + "fork": "anvil --fork-url ${0:-mainnet} --chain-id 31337 --config-out localhost.json", "compile": "forge compile", "generate": "node script/generateAccount.js", "deploy": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy && node script/generateTsAbis.js", diff --git a/templates/extensions/hardhat/packages/hardhat/hardhat.config.ts b/templates/extensions/hardhat/packages/hardhat/hardhat.config.ts index 33b39b6ff..445143c33 100644 --- a/templates/extensions/hardhat/packages/hardhat/hardhat.config.ts +++ b/templates/extensions/hardhat/packages/hardhat/hardhat.config.ts @@ -78,6 +78,14 @@ const config: HardhatUserConfig = { url: `https://polygon-mumbai.g.alchemy.com/v2/${providerApiKey}`, accounts: [deployerPrivateKey], }, + polygonZkEvm: { + url: `https://polygonzkevm-mainnet.g.alchemy.com/v2/${providerApiKey}`, + accounts: [deployerPrivateKey], + }, + polygonZkEvmTestnet: { + url: `https://polygonzkevm-testnet.g.alchemy.com/v2/${providerApiKey}`, + accounts: [deployerPrivateKey], + }, zkSyncTestnet: { url: "https://testnet.era.zksync.dev", zksync: true, @@ -106,6 +114,10 @@ const config: HardhatUserConfig = { url: "https://goerli.base.org", accounts: [deployerPrivateKey], }, + scrollSepolia: { + url: "https://sepolia-rpc.scroll.io", + accounts: [deployerPrivateKey], + }, }, verify: { etherscan: { diff --git a/templates/extensions/hardhat/packages/hardhat/package.json b/templates/extensions/hardhat/packages/hardhat/package.json index 5a04e6ac1..0c9eb361e 100644 --- a/templates/extensions/hardhat/packages/hardhat/package.json +++ b/templates/extensions/hardhat/packages/hardhat/package.json @@ -36,7 +36,7 @@ "eslint-config-prettier": "~8.5.0", "eslint-plugin-prettier": "~4.2.1", "ethers": "~5.7.1", - "hardhat": "~2.11.2", + "hardhat": "~2.17.3", "hardhat-deploy": "~0.11.31", "hardhat-gas-reporter": "~1.0.9", "prettier": "~2.8.4",