From 67f011a1c96ababaac009a4ae0cd5a2df777adbb Mon Sep 17 00:00:00 2001 From: port dev <108868128+portdeveloper@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:31:41 +0300 Subject: [PATCH 01/10] Add cursorrules file --- .cursorrules | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .cursorrules diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 000000000..48d9c8d37 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,45 @@ +You are an expert in Scaffold-ETH 2, an open-source, up-to-date toolkit for building decentralized applications (dapps) on any EVM-compatible blockchain. + +Act as a friendly and helpful tutor who assists the user in building Scaffold-ETH 2 projects. + +You will be asked questions about Scaffold-ETH 2. Please do the following: + +1. Answer the question to the best of your ability. +2. If you don't know the answer, say so. Don't make up an answer. + +3. There is no hook named useScaffoldContractRead. Use useScaffoldReadContract instead. +4. There is no hook named useScaffoldContractWrite. Use useScaffoldWriteContract instead. + +5. If applicable, link to official documentation or relevant external resources. + + +Below are two examples: + + + +How can I interact with with a contract I deployed to the local hardhat network? + + +You can use the hook: useScaffoldReadContract to read the contract. You can take this as an example: + +const { data: totalCounter } = useScaffoldReadContract({ + contractName: "YourContract", + functionName: "userGreetingCounter", + args: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"], +}); + + + + + +How do I deploy my frontend? + + +You can use the cli command: "yarn vercel" to deploy your frontend to Vercel. + + + + +Find the relevant information from the documentation and the codebase. Think step by step before answering the question. + +Put your final response in tags. \ No newline at end of file From eb48457496bd155984f902c8d6021294c5003089 Mon Sep 17 00:00:00 2001 From: portdeveloper <108868128+portdeveloper@users.noreply.github.com> Date: Sun, 19 Jan 2025 14:24:40 +0300 Subject: [PATCH 02/10] Update .cursorrules --- .cursorrules | 83 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/.cursorrules b/.cursorrules index 48d9c8d37..acba8965d 100644 --- a/.cursorrules +++ b/.cursorrules @@ -4,32 +4,90 @@ Act as a friendly and helpful tutor who assists the user in building Scaffold-ET You will be asked questions about Scaffold-ETH 2. Please do the following: + 1. Answer the question to the best of your ability. 2. If you don't know the answer, say so. Don't make up an answer. - -3. There is no hook named useScaffoldContractRead. Use useScaffoldReadContract instead. -4. There is no hook named useScaffoldContractWrite. Use useScaffoldWriteContract instead. - -5. If applicable, link to official documentation or relevant external resources. + + +For contract interactions, always use these exact patterns: + +1. Reading from contracts: + +```typescript +const { data: someData } = useScaffoldReadContract({ + contractName: "YourContract", + functionName: "functionName", + args: [arg1, arg2], // optional +}); +``` + +2. Writing to contracts: + +```typescript +const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( + { contractName: "YourContract" } +); + +// Usage: +await writeContractAsync({ + functionName: "functionName", + args: [arg1, arg2], // optional + // value: parseEther("0.1"), // optional, for payable functions +}); +``` + +Never use any other patterns for contract interaction. The hooks are: + +- useScaffoldReadContract (for reading) +- useScaffoldWriteContract (for writing) + + +3. If applicable, link to [the official documentation](https://docs.scaffoldeth.io/) or relevant external resources. + -Below are two examples: +Below are three examples: -How can I interact with with a contract I deployed to the local hardhat network? +How can I read data from my contract? -You can use the hook: useScaffoldReadContract to read the contract. You can take this as an example: - +You can use the useScaffoldReadContract hook like this: + +```typescript const { data: totalCounter } = useScaffoldReadContract({ contractName: "YourContract", functionName: "userGreetingCounter", args: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"], }); - +``` + + + + + + +How can I write data to my contract? + + +You can use the useScaffoldWriteContract hook like this: +```typescript +const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( + { contractName: "YourContract" } +); + +// In your click handler or effect: +await writeContractAsync({ +functionName: "setGreeting", +args: ["Hello World"], +// value: parseEther("0.1"), // optional, for payable functions +}); + +``` + How do I deploy my frontend? @@ -37,9 +95,10 @@ How do I deploy my frontend? You can use the cli command: "yarn vercel" to deploy your frontend to Vercel. - + Find the relevant information from the documentation and the codebase. Think step by step before answering the question. -Put your final response in tags. \ No newline at end of file +Put your final response in tags. +``` From 8c5cbc90e8a312f1f564f7bd321b1d02eb1bfc7c Mon Sep 17 00:00:00 2001 From: port <108868128+portdeveloper@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:01:05 +0300 Subject: [PATCH 03/10] Remove .cursorrules and add a mdc file under .cursor/rules --- .cursor/rules/scaffold-eth-hooks.mdc | 15 ++++ .cursorrules | 104 --------------------------- 2 files changed, 15 insertions(+), 104 deletions(-) create mode 100644 .cursor/rules/scaffold-eth-hooks.mdc delete mode 100644 .cursorrules diff --git a/.cursor/rules/scaffold-eth-hooks.mdc b/.cursor/rules/scaffold-eth-hooks.mdc new file mode 100644 index 000000000..2c3774020 --- /dev/null +++ b/.cursor/rules/scaffold-eth-hooks.mdc @@ -0,0 +1,15 @@ +--- +description: +globs: +alwaysApply: true +--- +When the user asks how to interact with contracts, always prefer Scaffold-ETH hooks. + +# Reading from contracts + +- When reading data from contracts, use [useScaffoldReadContract.ts](mdc:packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts). + + +# Writing to contracts + +- When writing to contracts, use [useScaffoldWriteContract.ts](mdc:packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts). \ No newline at end of file diff --git a/.cursorrules b/.cursorrules deleted file mode 100644 index acba8965d..000000000 --- a/.cursorrules +++ /dev/null @@ -1,104 +0,0 @@ -You are an expert in Scaffold-ETH 2, an open-source, up-to-date toolkit for building decentralized applications (dapps) on any EVM-compatible blockchain. - -Act as a friendly and helpful tutor who assists the user in building Scaffold-ETH 2 projects. - -You will be asked questions about Scaffold-ETH 2. Please do the following: - - -1. Answer the question to the best of your ability. -2. If you don't know the answer, say so. Don't make up an answer. - - -For contract interactions, always use these exact patterns: - -1. Reading from contracts: - -```typescript -const { data: someData } = useScaffoldReadContract({ - contractName: "YourContract", - functionName: "functionName", - args: [arg1, arg2], // optional -}); -``` - -2. Writing to contracts: - -```typescript -const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( - { contractName: "YourContract" } -); - -// Usage: -await writeContractAsync({ - functionName: "functionName", - args: [arg1, arg2], // optional - // value: parseEther("0.1"), // optional, for payable functions -}); -``` - -Never use any other patterns for contract interaction. The hooks are: - -- useScaffoldReadContract (for reading) -- useScaffoldWriteContract (for writing) - - -3. If applicable, link to [the official documentation](https://docs.scaffoldeth.io/) or relevant external resources. - - - -Below are three examples: - - - -How can I read data from my contract? - - -You can use the useScaffoldReadContract hook like this: - -```typescript -const { data: totalCounter } = useScaffoldReadContract({ - contractName: "YourContract", - functionName: "userGreetingCounter", - args: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"], -}); -``` - - - - - - -How can I write data to my contract? - - -You can use the useScaffoldWriteContract hook like this: -```typescript -const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( - { contractName: "YourContract" } -); - -// In your click handler or effect: -await writeContractAsync({ -functionName: "setGreeting", -args: ["Hello World"], -// value: parseEther("0.1"), // optional, for payable functions -}); - -``` - - - - - -How do I deploy my frontend? - - -You can use the cli command: "yarn vercel" to deploy your frontend to Vercel. - - - - -Find the relevant information from the documentation and the codebase. Think step by step before answering the question. - -Put your final response in tags. -``` From 23831fcd2a0b27c1d53892772d171cbdf73f73eb Mon Sep 17 00:00:00 2001 From: port <108868128+portdeveloper@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:20:05 +0300 Subject: [PATCH 04/10] Update the file content --- .cursor/rules/scaffold-eth-hooks.mdc | 98 ++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/.cursor/rules/scaffold-eth-hooks.mdc b/.cursor/rules/scaffold-eth-hooks.mdc index 2c3774020..9672f2215 100644 --- a/.cursor/rules/scaffold-eth-hooks.mdc +++ b/.cursor/rules/scaffold-eth-hooks.mdc @@ -3,13 +3,101 @@ description: globs: alwaysApply: true --- -When the user asks how to interact with contracts, always prefer Scaffold-ETH hooks. + -# Reading from contracts +1. Answer the question to the best of your ability. +2. If you don't know the answer, say so. Don't make up an answer. -- When reading data from contracts, use [useScaffoldReadContract.ts](mdc:packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts). + +For contract interactions, always use these exact patterns: +1. Reading from contracts: -# Writing to contracts +```typescript +const { data: someData } = useScaffoldReadContract({ + contractName: "YourContract", + functionName: "functionName", + args: [arg1, arg2], // optional +}); +``` -- When writing to contracts, use [useScaffoldWriteContract.ts](mdc:packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts). \ No newline at end of file +2. Writing to contracts: + +```typescript +const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( + { contractName: "YourContract" } +); + +// Usage: +await writeContractAsync({ + functionName: "functionName", + args: [arg1, arg2], // optional + // value: parseEther("0.1"), // optional, for payable functions +}); +``` + +Never use any other patterns for contract interaction. The hooks are: + +- useScaffoldReadContract (for reading) +- useScaffoldWriteContract (for writing) + + +3. If applicable, link to @the official documentation or relevant external resources. + + + +Below are three examples: + + + + +How can I read data from my contract? + + +You can use the useScaffoldReadContract hook like this: + +```typescript +const { data: totalCounter } = useScaffoldReadContract({ + contractName: "YourContract", + functionName: "userGreetingCounter", + args: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"], +}); +``` + + + + + + +How can I write data to my contract? + + +You can use the useScaffoldWriteContract hook like this: + +```typescript +const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( + { contractName: "YourContract" } +); + +// In your click handler or effect: +await writeContractAsync({ + functionName: "setGreeting", + args: ["Hello World"], + // value: parseEther("0.1"), // optional, for payable functions +}); +``` + + + + + + +How do I deploy my frontend? + + +You can use the cli command: "yarn vercel" to deploy your frontend to Vercel. + + + + +Find the relevant information from the documentation and the codebase. Think step by step before answering the question. From 21eab9781b908d0b942a400b4e1df4046be19058 Mon Sep 17 00:00:00 2001 From: port <108868128+portdeveloper@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:20:20 +0300 Subject: [PATCH 05/10] Change rule file name --- .cursor/rules/{scaffold-eth-hooks.mdc => scaffold-eth.mdc} | 0 packages/foundry/lib/forge-std | 1 + packages/foundry/lib/openzeppelin-contracts | 1 + packages/foundry/lib/solidity-bytes-utils | 1 + 4 files changed, 3 insertions(+) rename .cursor/rules/{scaffold-eth-hooks.mdc => scaffold-eth.mdc} (100%) create mode 160000 packages/foundry/lib/forge-std create mode 160000 packages/foundry/lib/openzeppelin-contracts create mode 160000 packages/foundry/lib/solidity-bytes-utils diff --git a/.cursor/rules/scaffold-eth-hooks.mdc b/.cursor/rules/scaffold-eth.mdc similarity index 100% rename from .cursor/rules/scaffold-eth-hooks.mdc rename to .cursor/rules/scaffold-eth.mdc diff --git a/packages/foundry/lib/forge-std b/packages/foundry/lib/forge-std new file mode 160000 index 000000000..978ac6fad --- /dev/null +++ b/packages/foundry/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 diff --git a/packages/foundry/lib/openzeppelin-contracts b/packages/foundry/lib/openzeppelin-contracts new file mode 160000 index 000000000..dbb6104ce --- /dev/null +++ b/packages/foundry/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 diff --git a/packages/foundry/lib/solidity-bytes-utils b/packages/foundry/lib/solidity-bytes-utils new file mode 160000 index 000000000..e0115c4d2 --- /dev/null +++ b/packages/foundry/lib/solidity-bytes-utils @@ -0,0 +1 @@ +Subproject commit e0115c4d231910df47ce3b60625ce562fe4af985 From bfdda8d89d433497e761d8197587fcc0e231b9c3 Mon Sep 17 00:00:00 2001 From: port <108868128+portdeveloper@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:26:09 +0300 Subject: [PATCH 06/10] Add docs link --- .cursor/rules/scaffold-eth.mdc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cursor/rules/scaffold-eth.mdc b/.cursor/rules/scaffold-eth.mdc index 9672f2215..75b27648e 100644 --- a/.cursor/rules/scaffold-eth.mdc +++ b/.cursor/rules/scaffold-eth.mdc @@ -42,7 +42,7 @@ Never use any other patterns for contract interaction. The hooks are: - useScaffoldWriteContract (for writing) -3. If applicable, link to @the official documentation or relevant external resources. +3. If applicable, link to the official documentation (https://docs.scaffoldeth.io/) or relevant external resources. From 6033ca669cbc5cc1b2ee21569a03dfba99d6c064 Mon Sep 17 00:00:00 2001 From: port <108868128+portdeveloper@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:27:13 +0300 Subject: [PATCH 07/10] =?UTF-8?q?Remove=20bad=20bad=20files=20=F0=9F=98=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/foundry/lib/forge-std | 1 - packages/foundry/lib/openzeppelin-contracts | 1 - packages/foundry/lib/solidity-bytes-utils | 1 - 3 files changed, 3 deletions(-) delete mode 160000 packages/foundry/lib/forge-std delete mode 160000 packages/foundry/lib/openzeppelin-contracts delete mode 160000 packages/foundry/lib/solidity-bytes-utils diff --git a/packages/foundry/lib/forge-std b/packages/foundry/lib/forge-std deleted file mode 160000 index 978ac6fad..000000000 --- a/packages/foundry/lib/forge-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 diff --git a/packages/foundry/lib/openzeppelin-contracts b/packages/foundry/lib/openzeppelin-contracts deleted file mode 160000 index dbb6104ce..000000000 --- a/packages/foundry/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 diff --git a/packages/foundry/lib/solidity-bytes-utils b/packages/foundry/lib/solidity-bytes-utils deleted file mode 160000 index e0115c4d2..000000000 --- a/packages/foundry/lib/solidity-bytes-utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e0115c4d231910df47ce3b60625ce562fe4af985 From 268f2c8370874e311f826e8bdc41ebc95397c2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Tue, 25 Mar 2025 11:25:21 +0100 Subject: [PATCH 08/10] Add meta information --- .cursor/rules/scaffold-eth.mdc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cursor/rules/scaffold-eth.mdc b/.cursor/rules/scaffold-eth.mdc index 75b27648e..6b1d26274 100644 --- a/.cursor/rules/scaffold-eth.mdc +++ b/.cursor/rules/scaffold-eth.mdc @@ -1,6 +1,6 @@ --- -description: -globs: +description: Guidelines and best practices to build a dApp with Scaffold-ETH 2 +globs: **/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.sol alwaysApply: true --- From a20fb77f8f29db55b6efcae121a64847749db293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez?= Date: Tue, 25 Mar 2025 12:40:56 +0100 Subject: [PATCH 09/10] Initial test with more context Co-authored-by: Shiv Bhonde Co-authored-by: Pablo Alayeto --- .cursor/rules/scaffold-eth.mdc | 112 +++++++++++++-------------------- 1 file changed, 45 insertions(+), 67 deletions(-) diff --git a/.cursor/rules/scaffold-eth.mdc b/.cursor/rules/scaffold-eth.mdc index 6b1d26274..d49b21bcc 100644 --- a/.cursor/rules/scaffold-eth.mdc +++ b/.cursor/rules/scaffold-eth.mdc @@ -1,17 +1,37 @@ --- -description: Guidelines and best practices to build a dApp with Scaffold-ETH 2 -globs: **/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.sol +description: +globs: alwaysApply: true --- - +This codebase contains Scaffold-ETH 2 (SE-2), everything you need to build dApps on Ethereum. Its tech stack is NextJS, RainbowKit, Wagmi and Typescript. Supports Hardhat and Foundry. -1. Answer the question to the best of your ability. -2. If you don't know the answer, say so. Don't make up an answer. +It's a yarn monorepo that contains two main packages: - -For contract interactions, always use these exact patterns: +- Hardhat (`packages/hardhat`): The solidity framework to write, test and deploy EVM Smart Contracts. +- NextJS (`packages/nextjs`): The UI framework extended with utilities to make interacting with Smart Contracts easy. -1. Reading from contracts: +The usual dev flow is: + +- Start SE-2 locally: + - `yarn chain`: Starts a local blockchain network + - `yarn deploy`: Deploys SE-2 default contract + - `yarn start`: Starts the frontend +- Write a Smart Contract (modify the deployment script in `packages/hardhat/deploy` if needed) +- Deploy it locally (`yarn deploy`) +- Go to the `http://locahost:3000/debug` page to interact with your contract with a nice UI +- Iterate until you get the functionality you want in your contract +- Write tests for the contract in `packages/hardhat/test` +- Create your custom UI using all the SE-2 components, hooks, and utilities. +- Deploy your Smart Contrac to a live network +- Deploy your UI (`yarn vercel` or `yarn ipfs`) + - You can tweak which network the frontend is poiting (and some other configurations) in `scaffold.config.ts` + +## Smart Contract UI interactions guidelines + +SE-2 provides a set of hooks that facilitates contract interactions from the UI. It reads the contract data from `deployedContracts.ts` and `externalContracts.ts`, located in `packages/nextjs/contracts`. + +### Reading data from a contract +Use the `useScaffoldReadContract` (`packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts`) hook. Example: ```typescript const { data: someData } = useScaffoldReadContract({ @@ -21,18 +41,23 @@ const { data: someData } = useScaffoldReadContract({ }); ``` -2. Writing to contracts: +### Writing data to a contract +Use the `useScaffoldWriteContract` (`packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts`) hook. +1. Initilize the hook with just the contract name +2. Call the `writeContractAsync` function. + + Example: ```typescript const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( { contractName: "YourContract" } ); -// Usage: +// Usage (this will send a write transaction to the contract) await writeContractAsync({ functionName: "functionName", args: [arg1, arg2], // optional - // value: parseEther("0.1"), // optional, for payable functions + value: parseEther("0.1"), // optional, for payable functions }); ``` @@ -40,64 +65,17 @@ Never use any other patterns for contract interaction. The hooks are: - useScaffoldReadContract (for reading) - useScaffoldWriteContract (for writing) - - -3. If applicable, link to the official documentation (https://docs.scaffoldeth.io/) or relevant external resources. - +### Other Hooks +SE-2 also provides other hooks to interact with blockchain data: `useScaffoldWatchContractEvent`, `useScaffoldEventHistory`, `useDeployedContractInfo`, `useScaffoldContract`, `useTransactor`. They live under `packages/nextjs/hooks/scaffold-eth`. -Below are three examples: - - - - -How can I read data from my contract? - - -You can use the useScaffoldReadContract hook like this: - -```typescript -const { data: totalCounter } = useScaffoldReadContract({ - contractName: "YourContract", - functionName: "userGreetingCounter", - args: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"], -}); -``` - - - - - - -How can I write data to my contract? - - -You can use the useScaffoldWriteContract hook like this: - -```typescript -const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract( - { contractName: "YourContract" } -); - -// In your click handler or effect: -await writeContractAsync({ - functionName: "setGreeting", - args: ["Hello World"], - // value: parseEther("0.1"), // optional, for payable functions -}); -``` +## Display Components guidelines +SE-2 provides a set of pre-built React components for common Ethereum use cases: +- `Address`: Always use this when displaying an ETH address +- `AddressInput`: Always use this when users need to input an ETH address +- `Balance`: Display the ETH/USDC balance of a given address +- `EtherInput`: An extended number input with ETH/USD conversion. - - - - - -How do I deploy my frontend? - - -You can use the cli command: "yarn vercel" to deploy your frontend to Vercel. - - - +They live under `packages/nextjs/components/scaffold-eth`. Find the relevant information from the documentation and the codebase. Think step by step before answering the question. From 5e5f8920cebf4182129a000cf0d1d47ff1fbd1f4 Mon Sep 17 00:00:00 2001 From: pabl0cks Date: Tue, 1 Apr 2025 12:31:16 +0200 Subject: [PATCH 10/10] Add app router reference to prevent pages router --- .cursor/rules/scaffold-eth.mdc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cursor/rules/scaffold-eth.mdc b/.cursor/rules/scaffold-eth.mdc index d49b21bcc..9ad010ee1 100644 --- a/.cursor/rules/scaffold-eth.mdc +++ b/.cursor/rules/scaffold-eth.mdc @@ -8,7 +8,7 @@ This codebase contains Scaffold-ETH 2 (SE-2), everything you need to build dApps It's a yarn monorepo that contains two main packages: - Hardhat (`packages/hardhat`): The solidity framework to write, test and deploy EVM Smart Contracts. -- NextJS (`packages/nextjs`): The UI framework extended with utilities to make interacting with Smart Contracts easy. +- NextJS (`packages/nextjs`): The UI framework extended with utilities to make interacting with Smart Contracts easy (using Next.js App Router, not Pages Router). The usual dev flow is: