From 0891805c86dad9d597feb1e8320d8fbbd5a73664 Mon Sep 17 00:00:00 2001 From: Nadai2010 Date: Mon, 18 Nov 2024 10:46:57 +0000 Subject: [PATCH 1/7] Sync Speedrun with last version Scaffold Stark in PR-355 --- .husky/pre-commit | 13 - CONTRIBUTING.md | 6 +- .../app/debug/_components/contract/Array.tsx | 31 +- .../_components/contract/ContractInput.tsx | 21 +- .../_components/contract/DisplayVariable.tsx | 10 +- .../contract/ReadOnlyFunctionForm.tsx | 35 +- .../app/debug/_components/contract/Struct.tsx | 9 +- .../debug/_components/contract/TxReceipt.tsx | 20 +- .../contract/WriteOnlyFunctionForm.tsx | 43 +- .../contract/__test__/mock/mockABI.ts | 490 ++++++++++++++++++ .../contract/__test__/utilsContract.test.tsx | 193 +++++++ .../contract/__test__/utilsDisplay.test.tsx | 252 +++++++++ .../_components/contract/utilsContract.tsx | 163 +++++- .../_components/contract/utilsDisplay.tsx | 317 ++++++++--- .../components/scaffold-stark/Address.tsx | 15 +- .../CustomConnectButton/ConnectModal.tsx | 1 + .../components/scaffold-stark/Input/utils.ts | 85 +-- .../seed/mockDeployedContractData.ts | 302 +++++++++++ .../__tests__/useScaffoldEventHistory.test.ts | 194 +++++++ .../useScaffoldMultiWriteContract.test.ts | 39 +- .../useScaffoldWriteContract.test.ts | 147 ++++++ .../hooks/scaffold-stark/useAutoConnect.ts | 7 +- .../scaffold-stark/useScaffoldEventHistory.ts | 136 +---- .../useScaffoldMultiWriteContract.ts | 76 ++- .../scaffold-stark/useScaffoldReadContract.ts | 2 +- .../useScaffoldWriteContract.ts | 152 +++--- packages/nextjs/package.json | 3 +- packages/nextjs/services/web3/connectors.tsx | 2 + .../nextjs/utils/scaffold-stark/common.ts | 12 +- .../nextjs/utils/scaffold-stark/contract.ts | 167 +----- packages/nextjs/utils/scaffold-stark/types.ts | 4 +- packages/snfoundry/package.json | 2 +- .../snfoundry/scripts-ts/deploy-contract.ts | 35 +- packages/snfoundry/scripts-ts/deploy.ts | 13 +- .../scripts-ts/helpers/deploy-wrapper.ts | 33 +- yarn.lock | 10 - 36 files changed, 2366 insertions(+), 674 deletions(-) create mode 100644 packages/nextjs/app/debug/_components/contract/__test__/mock/mockABI.ts create mode 100644 packages/nextjs/app/debug/_components/contract/__test__/utilsContract.test.tsx create mode 100644 packages/nextjs/app/debug/_components/contract/__test__/utilsDisplay.test.tsx create mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/seed/mockDeployedContractData.ts create mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldEventHistory.test.ts create mode 100644 packages/nextjs/hooks/scaffold-stark/__tests__/useScaffoldWriteContract.test.ts diff --git a/.husky/pre-commit b/.husky/pre-commit index fa2cd8d7..de5163db 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,17 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -DESIRED_COMMIT="46e0ec032956f0e7cbe0330f32b6b31eff824087" - -cd packages/snfoundry/local-devnet - -LAST_COMMIT=$(git log -1 --format="%H") - -if [ "$LAST_COMMIT" != "$DESIRED_COMMIT" ]; then - echo "FAIL: Last local-devnet commit ($LAST_COMMIT) is not the desired ($DESIRED_COMMIT)." - exit 1 -fi - -cd - - npm run format:check diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6e589a2..2da73951 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,14 @@ # Contributing to Scaffold-Stark -Thank you for your interest in contributing to Scaffold-Stark! Your support enhances this StarkNet-focused framework that bridges smart contract integration with web applications. +Thank you for your interest in contributing to Scaffold-Stark! Your support enhances this Starknet-focused framework that bridges smart contract integration with web applications. ## About the Project -**Scaffold-Stark** provides a full DApp development template, offering seamless integration of StarkNet smart contracts with web applications. **SpeedrunStark.com** offers interactive challenges that serve to practice your Cairo and StarkNet skills, learn how to use the provided hooks, and launch your applications swiftly with potential rewards. +**Scaffold-Stark** provides a full DApp development template, offering seamless integration of Starket smart contracts with web applications. **SpeedrunStark.com** offers interactive challenges that serve to practice your Cairo and Starknet skills, learn how to use the provided hooks, and launch your applications swiftly with potential rewards. ## Project Vision -Our goal is to simplify the DApp development and learning process, enabling developers to launch applications efficiently while understanding the intricacies of StarkNet. +Our goal is to simplify the DApp development and learning process, enabling developers to launch applications efficiently while understanding the intricacies of Starknet. ## How to Get Involved diff --git a/packages/nextjs/app/debug/_components/contract/Array.tsx b/packages/nextjs/app/debug/_components/contract/Array.tsx index d5803960..43a5d15f 100644 --- a/packages/nextjs/app/debug/_components/contract/Array.tsx +++ b/packages/nextjs/app/debug/_components/contract/Array.tsx @@ -9,6 +9,7 @@ import { replacer } from "~~/utils/scaffold-stark/common"; import { ContractInput } from "./ContractInput"; import { Abi } from "abi-wan-kanabi"; import { parseGenericType } from "~~/utils/scaffold-stark"; +import { FormErrorMessageState } from "./utilsDisplay"; type ArrayProps = { abi: Abi; @@ -16,7 +17,7 @@ type ArrayProps = { parentForm: Record | undefined; setParentForm: (form: Record) => void; parentStateObjectKey: string; - setFormErrorMessage: Dispatch>; + setFormErrorMessage: Dispatch>; }; export const ArrayInput = ({ @@ -67,23 +68,21 @@ export const ArrayInput = ({ | Record | ((arg: Record) => void), ) => { - let nextInputObject: Record = nextInputRecipe; + // if we find a function (a.k.a setState recipe), we run it to generate the next state based on recpe, else just use the object passed in + const nextInputObject: Record = + typeof nextInputRecipe === "function" + ? nextInputRecipe(parentForm!) + : nextInputRecipe; - // set state recipe function, handle - if (typeof nextInputRecipe === "function") { - nextInputObject = nextInputRecipe(parentForm!); - } - - const currentInputArray = { ...inputArr }; - - // we do some nasty workaround - currentInputArray[index] = - nextInputObject?.[`input_${index}`] || null; - - setInputArr(currentInputArray); + setInputArr((currentInputArray: any) => { + return { + ...currentInputArray, + [index]: nextInputObject?.[index] || null, + }; + }); }} - form={inputArr[index]} - stateObjectKey={`input_${index}`} + form={inputArr} + stateObjectKey={index} paramType={ { name: `${abiParameter.name}[${index}]`, diff --git a/packages/nextjs/app/debug/_components/contract/ContractInput.tsx b/packages/nextjs/app/debug/_components/contract/ContractInput.tsx index 9cde96ed..4fa53bdf 100644 --- a/packages/nextjs/app/debug/_components/contract/ContractInput.tsx +++ b/packages/nextjs/app/debug/_components/contract/ContractInput.tsx @@ -3,11 +3,17 @@ import { Dispatch, SetStateAction } from "react"; import { InputBase, IntegerInput } from "~~/components/scaffold-stark"; import { AbiParameter } from "~~/utils/scaffold-stark/contract"; -import { displayType } from "./utilsDisplay"; +import { + addError, + clearError, + displayType, + FormErrorMessageState, +} from "./utilsDisplay"; import { isCairoArray, isCairoBigInt, isCairoInt, + isCairoTuple, isCairoType, isCairoU256, } from "~~/utils/scaffold-stark"; @@ -21,7 +27,7 @@ type ContractInputProps = { form: Record | undefined; stateObjectKey: string; paramType: AbiParameter; - setFormErrorMessage: Dispatch>; + setFormErrorMessage: Dispatch>; }; export const ContractInput = ({ @@ -58,6 +64,11 @@ export const ContractInput = ({ setFormErrorMessage={setFormErrorMessage} /> ); + } + + // we prio tuples here to avoid wrong input + else if (isCairoTuple(paramType.type)) { + return ; } else if ( isCairoInt(paramType.type) || isCairoBigInt(paramType.type) || @@ -68,7 +79,11 @@ export const ContractInput = ({ {...inputProps} variant={paramType.type} onError={(errMessage: string | null) => - setFormErrorMessage(errMessage) + setFormErrorMessage((prev) => { + if (!!errMessage) + return addError(prev, "intError" + stateObjectKey, errMessage); + return clearError(prev, "intError" + stateObjectKey); + }) } /> ); diff --git a/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx b/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx index d3f293ef..fa2849a8 100644 --- a/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx +++ b/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx @@ -9,7 +9,7 @@ import { Abi } from "abi-wan-kanabi"; import { Address } from "@starknet-react/chains"; import { useReadContract } from "@starknet-react/core"; import { BlockNumber } from "starknet"; -import { displayTxResult } from "./utilsDisplay"; +import { decodeContractResponse } from "./utilsDisplay"; import { useTheme } from "next-themes"; type DisplayVariableProps = { @@ -85,7 +85,13 @@ export const DisplayVariable = ({ showAnimation ? "bg-warning rounded-sm animate-pulse-fast" : "" }`} > - {displayTxResult(result, false, abiFunction?.outputs)} + {decodeContractResponse({ + resp: result, + abi, + functionOutputs: abiFunction?.outputs, + asText: true, + showAsString: true, + })} diff --git a/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx b/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx index ea641a83..ff5017f4 100644 --- a/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx +++ b/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx @@ -4,15 +4,18 @@ import { useState, useRef, useEffect } from "react"; import { Abi } from "abi-wan-kanabi"; import { Address } from "@starknet-react/chains"; import { - displayTxResult, getFunctionInputKey, getInitialFormState, - getParsedContractFunctionArgs, + getArgsAsStringInputFromForm, transformAbiFunction, + FormErrorMessageState, + isError, + getTopErrorMessage, + decodeContractResponse, } from "~~/app/debug/_components/contract"; import { AbiFunction } from "~~/utils/scaffold-stark/contract"; import { BlockNumber } from "starknet"; -import { useReadContract } from "@starknet-react/core"; +import { useContract, useReadContract } from "@starknet-react/core"; import { ContractInput } from "./ContractInput"; type ReadOnlyFunctionFormProps = { @@ -30,15 +33,21 @@ export const ReadOnlyFunctionForm = ({ getInitialFormState(abiFunction), ); const [inputValue, setInputValue] = useState(undefined); - const [formErrorMessage, setFormErrorMessage] = useState(null); + const [formErrorMessage, setFormErrorMessage] = + useState({}); const lastForm = useRef(form); + const { contract: contractInstance } = useContract({ + abi, + address: contractAddress, + }); + const { isFetching, data, refetch, error } = useReadContract({ address: contractAddress, functionName: abiFunction.name, abi: [...abi], args: inputValue || [], - enabled: !!inputValue, + enabled: !!inputValue && !!contractInstance, blockIdentifier: "pending" as BlockNumber, }); @@ -66,8 +75,7 @@ export const ReadOnlyFunctionForm = ({ }); const handleRead = () => { - const newInputValue = getParsedContractFunctionArgs(form, false, true); - + const newInputValue = getArgsAsStringInputFromForm(form); if (JSON.stringify(form) !== JSON.stringify(lastForm.current)) { setInputValue(newInputValue); lastForm.current = form; @@ -87,7 +95,12 @@ export const ReadOnlyFunctionForm = ({

Result:

-                {displayTxResult(data, false, abiFunction?.outputs)}
+                {decodeContractResponse({
+                  resp: data,
+                  abi,
+                  functionOutputs: abiFunction?.outputs,
+                  asText: true,
+                })}
               
)} @@ -95,15 +108,15 @@ export const ReadOnlyFunctionForm = ({
-
{displayTxResult(txResult, false)}
+
+            {decodeContractResponse({
+              resp: txResult,
+              abi: [],
+              functionOutputs: [],
+              asText: true,
+            })}
+          
diff --git a/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx b/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx index 46459431..e765b71d 100644 --- a/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx +++ b/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx @@ -6,14 +6,18 @@ import { // TxReceipt, getFunctionInputKey, getInitialFormState, - getParsedContractFunctionArgs, + getArgsAsStringInputFromForm, transformAbiFunction, + FormErrorMessageState, + getTopErrorMessage, + isError, } from "~~/app/debug/_components/contract"; import { useTargetNetwork } from "~~/hooks/scaffold-stark/useTargetNetwork"; import { useSendTransaction, useNetwork, useTransactionReceipt, + useContract, } from "@starknet-react/core"; import { Abi } from "abi-wan-kanabi"; import { AbiFunction } from "~~/utils/scaffold-stark/contract"; @@ -40,7 +44,8 @@ export const WriteOnlyFunctionForm = ({ const [form, setForm] = useState>(() => getInitialFormState(abiFunction), ); - const [formErrorMessage, setFormErrorMessage] = useState(null); + const [formErrorMessage, setFormErrorMessage] = + useState({}); const { status: walletStatus, isConnected, account, chainId } = useAccount(); const { chain } = useNetwork(); const writeTxn = useTransactor(); @@ -54,23 +59,17 @@ export const WriteOnlyFunctionForm = ({ [chain, targetNetwork.network, walletStatus], ); + const { contract: contractInstance } = useContract({ + abi, + address: contractAddress, + }); + const { data: result, isPending: isLoading, sendAsync, error, - } = useSendTransaction({ - calls: [ - { - contractAddress, - entrypoint: abiFunction.name, - - // use infinity to completely flatten array from n dimensions to 1 dimension - // writing in starknet next still needs rawArgs parsing, use v2 parsing - calldata: getParsedContractFunctionArgs(form, false).flat(Infinity), - }, - ], - }); + } = useSendTransaction({}); // side effect for error logging useEffect(() => { @@ -83,7 +82,17 @@ export const WriteOnlyFunctionForm = ({ const handleWrite = async () => { if (sendAsync) { try { - const makeWriteWithParams = () => sendAsync(); + const makeWriteWithParams = () => + sendAsync( + !!contractInstance + ? [ + contractInstance.populate( + abiFunction.name, + getArgsAsStringInputFromForm(form), + ), + ] + : [], + ); await writeTxn(makeWriteWithParams); onChange(); } catch (e: any) { @@ -131,7 +140,7 @@ export const WriteOnlyFunctionForm = ({ const errorMsg = (() => { if (writeDisabled) return "Wallet not connected or on wrong network"; - return formErrorMessage; + return getTopErrorMessage(formErrorMessage); })(); return ( @@ -163,7 +172,7 @@ export const WriteOnlyFunctionForm = ({ >