From c7bc34391d6a590deb77265dadde9983fad30e57 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 1 Apr 2024 21:09:41 -0500 Subject: [PATCH 01/54] feat: initial quickcode guide for deploying contract using hardhat or foundry --- .vscode/settings.json | 3 + bun.lockb | Bin 615244 -> 615027 bytes components/content/DeployContract.vue | 43 ++++++ components/content/EnvSetup.vue | 51 +++++++ components/content/IconList.vue | 46 +++++++ components/content/PageLinks.vue | 19 +++ components/content/WalletSetup.vue | 43 ++++++ content/10.getting-started/1.index.md | 83 ++++-------- .../_getting-started/_aPartial.md | 5 - .../_getting-started/_anotherPartial.md | 5 - .../_foundry_deploy_contract.md | 26 ++++ .../_hardhat_deploy_contract.md | 26 ++++ content/_partials/deploy_contract_foundry.md | 87 ++++++++++++ content/_partials/deploy_contract_hardhat.md | 126 ++++++++++++++++++ content/_partials/setting-up-your-wallet.md | 55 +++++++- cspell-config/cspell-dev.txt | 4 + cspell-config/cspell-zksync.txt | 1 + nuxt.config.ts | 2 +- package.json | 1 + 19 files changed, 551 insertions(+), 75 deletions(-) create mode 100644 components/content/DeployContract.vue create mode 100644 components/content/EnvSetup.vue create mode 100644 components/content/IconList.vue create mode 100644 components/content/PageLinks.vue create mode 100644 components/content/WalletSetup.vue delete mode 100644 content/10.getting-started/_getting-started/_aPartial.md delete mode 100644 content/10.getting-started/_getting-started/_anotherPartial.md create mode 100644 content/10.getting-started/_getting-started/_foundry_deploy_contract.md create mode 100644 content/10.getting-started/_getting-started/_hardhat_deploy_contract.md create mode 100644 content/_partials/deploy_contract_foundry.md create mode 100644 content/_partials/deploy_contract_hardhat.md diff --git a/.vscode/settings.json b/.vscode/settings.json index b95ecf4f..e61fdac9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,8 @@ "files.eol": "\n", "[markdown]": { "editor.wordWrap": "on" + }, + "editor.codeActionsOnSave": { + "source.fixAll.markdownlint": true } } diff --git a/bun.lockb b/bun.lockb index 3dd88caf09ef676341ed43d7856cdbbc210f8d8d..36a4beac67e6e78dc88d09f41bc7c1c683bf9332 100755 GIT binary patch delta 103165 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 delta 103585 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 UjH)Zv(!dPm{q18KR;$$hKMU)qi~s-t diff --git a/components/content/DeployContract.vue b/components/content/DeployContract.vue new file mode 100644 index 00000000..136b5d6d --- /dev/null +++ b/components/content/DeployContract.vue @@ -0,0 +1,43 @@ + + + diff --git a/components/content/EnvSetup.vue b/components/content/EnvSetup.vue new file mode 100644 index 00000000..9bd6db3f --- /dev/null +++ b/components/content/EnvSetup.vue @@ -0,0 +1,51 @@ + + + diff --git a/components/content/IconList.vue b/components/content/IconList.vue new file mode 100644 index 00000000..ad555d74 --- /dev/null +++ b/components/content/IconList.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/components/content/PageLinks.vue b/components/content/PageLinks.vue new file mode 100644 index 00000000..6a6fa3a5 --- /dev/null +++ b/components/content/PageLinks.vue @@ -0,0 +1,19 @@ + + + diff --git a/components/content/WalletSetup.vue b/components/content/WalletSetup.vue new file mode 100644 index 00000000..7ff271f7 --- /dev/null +++ b/components/content/WalletSetup.vue @@ -0,0 +1,43 @@ + + + diff --git a/content/10.getting-started/1.index.md b/content/10.getting-started/1.index.md index f7ab24ef..d6ffd040 100644 --- a/content/10.getting-started/1.index.md +++ b/content/10.getting-started/1.index.md @@ -1,79 +1,44 @@ --- -title: Introduction -description: Welcome to Nuxt UI Pro documentation template. +title: Deploy Smart Contracts on zkSync! +description: Learn to deploy smart contracts efficiently in the zkSync environment. layout: test --- -This template is a ready-to-use documentation template made with [Nuxt UI Pro](https://ui.nuxt.com/pro), a collection of -premium components built on top of [Nuxt UI](https://ui.nuxt.com) to create beautiful & responsive Nuxt applications in -minutes. +Welcome to the Quickstart Guide for deploying smart contracts on zkSync! In this guide, we'll walk you through the process of creating and deploying a simple smart contract that holds an adventure for Zeek. -::content-switcher +::icon-list --- -items: [{ - label: 'HardHat', - partial: '_getting-started/_aPartial' -}, { - label: 'Forge', - partial: '_getting-started/_anotherpartial' -}] --- :: -There are already many websites based on this template: +Let's dive in and start your developer journey on zkSync! + +## Quickstart framework selection -- [Nuxt](https://nuxt.com) - The Nuxt website -- [Nuxt UI](https://ui.nuxt.com) - The documentation of `@nuxt/ui` and `@nuxt/ui-pro` -- [Nuxt Image](https://image.nuxt.com) - The documentation of `@nuxt/image` -- [Nuxt Content](https://content.nuxt.com) - The documentation of `@nuxt/content` -- [Nuxt Devtools](https://devtools.nuxt.com) - The documentation of `@nuxt/devtools` -- [Nuxt Studio](https://nuxt.studio) - The pro version of Nuxt Content +Select the framework you want to get started using zkSync Era with. -::display-partial +::content-switcher --- -partial: 'Setting up your wallet' +items: [{ + label: 'HardHat', + partial: '_getting-started/_hardhat_deploy_contract' +}, { + label: 'Foundry', + partial: '_getting-started/_foundry_deploy_contract' +}] --- :: -## Features -- Powered by [Nuxt 3](https://nuxt.com) -- Built with [Nuxt UI](https://ui.nuxt.com) and [Nuxt UI Pro](https://ui.nuxt.com/pro) -- Write content with [MDC syntax](https://content.nuxt.com/usage/markdown) thanks to - [Nuxt Content](https://content.nuxt.com) -- Compatible with [Nuxt Studio](https://nuxt.studio) -- Auto-generated sidebar navigation -- Full-Text Search out of the box -- Beautiful Typography styles -- Dark mode support -- And more... +## Takeaways -## Play online +- zkSync is EVM compatible and you can write smart contracts in Solidity or Vyper. +- zkSync supports your favorite development toolkit Hardhat and Foundry. +- zkSync CLI provides a quick way to scaffold different types of projects thanks to its multiple templates. +- Contracts deployed to zkSync are compiled using `zksolc` or `zkvyper` as they generate a special bytecode for zkSync's ZKEVM. -You can start playing with this template in your browser using our online sandboxes: +## Next steps -::card ---- -title: Play on StackBlitz -icon: i-simple-icons-stackblitz -to: https://stackblitz.com/github/nuxt-ui-pro/docs/ -target: _blank ---- -Explore Nuxt built-in components for pages, layouts, head, and more. -:: - ---- - -::card ---- -title: Play on CodeSandbox -icon: i-simple-icons-codesandbox -to: https://codesandbox.io/s/github/nuxt-ui-pro/docs/ -target: _blank ---- -Explore Nuxt built-in components for pages, layouts, head, and more. -:: - ---- +This was your first step towards becoming a zkSync developer. Here is what you can do next: -Or open [Nuxt UI playground](https://ui.nuxt.com/playground). +- Let’s take our `ZeekAdventure.sol` contract and create a contract factory! diff --git a/content/10.getting-started/_getting-started/_aPartial.md b/content/10.getting-started/_getting-started/_aPartial.md deleted file mode 100644 index 0fea5446..00000000 --- a/content/10.getting-started/_getting-started/_aPartial.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: A Partial ---- - -This is a partial that's coming from somewhere else! diff --git a/content/10.getting-started/_getting-started/_anotherPartial.md b/content/10.getting-started/_getting-started/_anotherPartial.md deleted file mode 100644 index f1ac3572..00000000 --- a/content/10.getting-started/_getting-started/_anotherPartial.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Another Partial ---- - -This is the second partial diff --git a/content/10.getting-started/_getting-started/_foundry_deploy_contract.md b/content/10.getting-started/_getting-started/_foundry_deploy_contract.md new file mode 100644 index 00000000..159b16d7 --- /dev/null +++ b/content/10.getting-started/_getting-started/_foundry_deploy_contract.md @@ -0,0 +1,26 @@ +--- +title: Foundry | Deploy Contract +--- + +`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. It extends Foundry's capabilities for Ethereum app development to support zkSync, allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. + +## Step 1: Setting up environment (todo - update) +::env-setup +--- + +--- +:: + +## Step 2: Set up wallet +::wallet-setup +--- + +--- +:: + +## Step 3: Deploying your first contract +::display-partial +--- +partial: 'Deploy contract using Foundry' +--- +:: diff --git a/content/10.getting-started/_getting-started/_hardhat_deploy_contract.md b/content/10.getting-started/_getting-started/_hardhat_deploy_contract.md new file mode 100644 index 00000000..7f0644dd --- /dev/null +++ b/content/10.getting-started/_getting-started/_hardhat_deploy_contract.md @@ -0,0 +1,26 @@ +--- +title: Hardhat | Deploy Contract +--- + +Hardhat is an Ethereum development environment, designed for easy smart contract development in Solidity. zkSync provides its own plugins which makes working with contracts on zkSync simple and efficient. + +## Step 1: Setting up environment +::env-setup +--- + +--- +:: + +## Step 2: Set up wallet +::wallet-setup +--- + +--- +:: + +## Step 3: Deploying your first contract +::display-partial +--- +partial: 'Deploy contract using Hardhat' +--- +:: diff --git a/content/_partials/deploy_contract_foundry.md b/content/_partials/deploy_contract_foundry.md new file mode 100644 index 00000000..c84a71a9 --- /dev/null +++ b/content/_partials/deploy_contract_foundry.md @@ -0,0 +1,87 @@ +--- +title: Deploy contract using Foundry +--- + +Now that we have setup our environment and our wallet, we are ready to deploy our first contract! For this tutorial we'll focus on the `/src/ZeekAdventures.sol` contract: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title ZeekAdventures + * @dev Embark on adventures with Zeek, the playful cat mascot. This contract allows + * setting and retrieving Zeek's current adventure. + */ +contract ZeekAdventures { + // Zeek's current adventure + string private adventure; + + // Event to announce when Zeek's adventure is set + event AdventureSet(string newAdventure); + + /** + * @dev Constructor to set the initial adventure upon deployment. + * @param _initialAdventure The initial adventure Zeek will embark on. + */ + constructor(string memory _initialAdventure) { + adventure = _initialAdventure; + emit AdventureSet(_initialAdventure); + } + + /** + * @dev Sets a new adventure for Zeek. + * @param _newAdventure The new adventure Zeek will embark on. + */ + function setAdventure(string memory _newAdventure) public { + adventure = _newAdventure; + emit AdventureSet(_newAdventure); + } + + /** + * @dev Returns the current adventure Zeek is on. + * @return The current adventure of Zeek. + */ + function currentAdventure() public view returns (string memory) { + return adventure; + } +} +``` + +### Compile contract + +To compile the contracts in the project, run the following command: + +```bash +forge build --zksync +``` + +**You'll get the following output:** + +```bash +TODO +``` + +The compiled artifacts will be located in the `/zkout` folder. + +### Deploy + +In this section, we’ll get to deploy the ZeekAdventure contract onto {network}. The script to deploy contracts is found at `/scripts/deploy.s.sol`. + +// TODO: +@Dustin +Write foundry script for deployment. Add and explain here. + +To deploy the contract, run the following command: + +```bash +forge script command --zksync +``` + +**You'll get the following output:** + +```bash +TODO +``` + +🥳 Congratulations! You just deployed a smart contract to {network}! \ No newline at end of file diff --git a/content/_partials/deploy_contract_hardhat.md b/content/_partials/deploy_contract_hardhat.md new file mode 100644 index 00000000..a63d06c2 --- /dev/null +++ b/content/_partials/deploy_contract_hardhat.md @@ -0,0 +1,126 @@ +--- +title: Deploy contract using Hardhat +--- + +Now that we have setup our environment and our wallet, we are ready to deploy our first contract! For this tutorial we'll focus on the `/contracts/ZeekAdventures.sol` contract: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title ZeekAdventures + * @dev Embark on adventures with Zeek, the playful cat mascot. This contract allows + * setting and retrieving Zeek's current adventure. + */ +contract ZeekAdventures { + // Zeek's current adventure + string private adventure; + + // Event to announce when Zeek's adventure is set + event AdventureSet(string newAdventure); + + /** + * @dev Constructor to set the initial adventure upon deployment. + * @param _initialAdventure The initial adventure Zeek will embark on. + */ + constructor(string memory _initialAdventure) { + adventure = _initialAdventure; + emit AdventureSet(_initialAdventure); + } + + /** + * @dev Sets a new adventure for Zeek. + * @param _newAdventure The new adventure Zeek will embark on. + */ + function setAdventure(string memory _newAdventure) public { + adventure = _newAdventure; + emit AdventureSet(_newAdventure); + } + + /** + * @dev Returns the current adventure Zeek is on. + * @return The current adventure of Zeek. + */ + function currentAdventure() public view returns (string memory) { + return adventure; + } +} +``` + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compilers. For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +::code-group + +```bash [yarn] +yarn compile:contracts +``` + +```bash [pnpm] +pnpm run compile:contracts +``` + +```bash [npm] +npm run compile:contracts +``` + +```bash [bun] +bun run compile:contracts +``` +:: + +**You'll get the following output:** + +```bash +Compiling contracts for zkSync Era with zksolc v1.4.1 and solc v0.8.23 +Compiling 1 Solidity files +Successfully compiled 1 Solidity files +✨ Done in 1.55s. +``` + +The compiled artifacts will be located in the `/artifacts-zk` folder. + +### Deploy + +In this section, we’ll get to deploy the ZeekAdventure contract onto {network}. The script to deploy contracts is found at `/deploy/deploy.ts`. + +The `contractArtifactName` defines the contract we want to deploy.Here we have it set to our contract, ZeekAdventure. Similarly, `constructorArguments` are the arguments we need to provide to the constructor to initialize the contract. + +To deploy the contract, run the following command: + +::code-group + +```bash [yarn] +yarn deploy +``` + +```bash [pnpm] +pnpm run deploy +``` + +```bash [npm] +npm run deploy +``` + +```bash [bun] +bun run deploy +``` +:: + +**You'll get the following output:** + +```bash +Starting deployment process of "ZeekAdventures"... +Estimated deployment cost: 0.0000648863 ETH + +"ZeekAdventures" was successfully deployed: + - Contract address: 0x0BaF96A7f137B05d0D35b76d59B16c86C1791D8D + - Contract source: contracts/ZeekAdventures.sol:ZeekAdventures + - Encoded constructor arguments: 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000 +``` + +🥳 Congratulations! You just deployed a smart contract to {network}! \ No newline at end of file diff --git a/content/_partials/setting-up-your-wallet.md b/content/_partials/setting-up-your-wallet.md index 31817176..c1070e6d 100644 --- a/content/_partials/setting-up-your-wallet.md +++ b/content/_partials/setting-up-your-wallet.md @@ -2,8 +2,53 @@ title: Setting up your wallet --- -Here's how to setup your wallet, follow the instructions below: - -1. Download wallet -2. ??? -3. Profit +
+

+ Deploying contracts on the zkSync Sepolia testnet requires having testnet ETH. If you're working within the local + development environment, you can utilize pre-configured rich wallets and skip this step. For testnet deployments, + follow these steps to secure your funds: +

+
    +
  1. + Obtaining Testnet ETH: + +
  2. +
  3. + Verify your balance: + +
  4. +
+
\ No newline at end of file diff --git a/cspell-config/cspell-dev.txt b/cspell-config/cspell-dev.txt index 079032b5..c29eb22f 100644 --- a/cspell-config/cspell-dev.txt +++ b/cspell-config/cspell-dev.txt @@ -6,3 +6,7 @@ otlp nvme nuxtdotjs fontaine +zksolc +zkvyper +ZKEVM +zkout diff --git a/cspell-config/cspell-zksync.txt b/cspell-config/cspell-zksync.txt index a4b6547b..ebc46595 100644 --- a/cspell-config/cspell-zksync.txt +++ b/cspell-config/cspell-zksync.txt @@ -2,3 +2,4 @@ matterlabs zksync boojum +Zeek diff --git a/nuxt.config.ts b/nuxt.config.ts index 1393e068..1700f8e6 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -16,7 +16,7 @@ export default defineNuxtConfig({ icons: { collections: { ...zksyncIcons, - ...getIconCollections(['heroicons', 'simple-icons']), + ...getIconCollections(['heroicons', 'simple-icons', 'zondicons']), }, }, }, diff --git a/package.json b/package.json index 02d16b01..015ae393 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "dependencies": { "@iconify-json/heroicons": "^1.1.20", "@iconify-json/simple-icons": "^1.1.95", + "@iconify-json/zondicons": "^1.1.8", "@nuxt/content": "^2.12.1", "@nuxt/fonts": "^0.3.0", "@nuxt/ui-pro": "^1.0.2", From fd75318937137124e1079f075df713475c72e191 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 1 Apr 2024 21:17:45 -0500 Subject: [PATCH 02/54] feat: initial quickcode guide for deploying contract using hardhat or foundry --- components/content/PageLinks.vue | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 components/content/PageLinks.vue diff --git a/components/content/PageLinks.vue b/components/content/PageLinks.vue deleted file mode 100644 index 6a6fa3a5..00000000 --- a/components/content/PageLinks.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - From cf516c378cf2d47c109f7a7dda66dd4965b8f3fd Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 1 Apr 2024 21:30:17 -0500 Subject: [PATCH 03/54] chore: update navigation --- content/10.getting-started/_dir.yml | 2 - .../1.index.md | 6 +- .../2.deploy-factory.md} | 2 +- .../3.usage.md => 10.quick-code/3.testing.md} | 2 +- content/10.quick-code/4.upgrading.md | 155 ++++++++++++++++++ content/10.quick-code/5.verifying.md | 155 ++++++++++++++++++ content/10.quick-code/6.paymaster.md | 155 ++++++++++++++++++ content/10.quick-code/7.smart-account.md | 155 ++++++++++++++++++ content/10.quick-code/_dir.yml | 2 + .../_quick-code}/_foundry_deploy_contract.md | 0 .../_quick-code}/_hardhat_deploy_contract.md | 0 11 files changed, 627 insertions(+), 7 deletions(-) delete mode 100644 content/10.getting-started/_dir.yml rename content/{10.getting-started => 10.quick-code}/1.index.md (88%) rename content/{10.getting-started/2.installation.md => 10.quick-code/2.deploy-factory.md} (96%) rename content/{10.getting-started/3.usage.md => 10.quick-code/3.testing.md} (99%) create mode 100644 content/10.quick-code/4.upgrading.md create mode 100644 content/10.quick-code/5.verifying.md create mode 100644 content/10.quick-code/6.paymaster.md create mode 100644 content/10.quick-code/7.smart-account.md create mode 100644 content/10.quick-code/_dir.yml rename content/{10.getting-started/_getting-started => 10.quick-code/_quick-code}/_foundry_deploy_contract.md (100%) rename content/{10.getting-started/_getting-started => 10.quick-code/_quick-code}/_hardhat_deploy_contract.md (100%) diff --git a/content/10.getting-started/_dir.yml b/content/10.getting-started/_dir.yml deleted file mode 100644 index c8496134..00000000 --- a/content/10.getting-started/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: Getting Started -layout: docs diff --git a/content/10.getting-started/1.index.md b/content/10.quick-code/1.index.md similarity index 88% rename from content/10.getting-started/1.index.md rename to content/10.quick-code/1.index.md index d6ffd040..62229116 100644 --- a/content/10.getting-started/1.index.md +++ b/content/10.quick-code/1.index.md @@ -1,5 +1,5 @@ --- -title: Deploy Smart Contracts on zkSync! +title: Smart Contracts description: Learn to deploy smart contracts efficiently in the zkSync environment. layout: test --- @@ -21,10 +21,10 @@ Select the framework you want to get started using zkSync Era with. --- items: [{ label: 'HardHat', - partial: '_getting-started/_hardhat_deploy_contract' + partial: '_quick-code/_hardhat_deploy_contract' }, { label: 'Foundry', - partial: '_getting-started/_foundry_deploy_contract' + partial: '_quick-code/_foundry_deploy_contract' }] --- :: diff --git a/content/10.getting-started/2.installation.md b/content/10.quick-code/2.deploy-factory.md similarity index 96% rename from content/10.getting-started/2.installation.md rename to content/10.quick-code/2.deploy-factory.md index 802d52fe..7d4d1eb5 100644 --- a/content/10.getting-started/2.installation.md +++ b/content/10.quick-code/2.deploy-factory.md @@ -1,5 +1,5 @@ --- -title: Installation +title: Contract Factories description: Get started with Nuxt UI Pro documentation template. layout: docs --- diff --git a/content/10.getting-started/3.usage.md b/content/10.quick-code/3.testing.md similarity index 99% rename from content/10.getting-started/3.usage.md rename to content/10.quick-code/3.testing.md index 6cdaaed6..0c940a7d 100644 --- a/content/10.getting-started/3.usage.md +++ b/content/10.quick-code/3.testing.md @@ -1,5 +1,5 @@ --- -title: Usage +title: Testing description: Learn how to write and customize your documentation. --- diff --git a/content/10.quick-code/4.upgrading.md b/content/10.quick-code/4.upgrading.md new file mode 100644 index 00000000..5999d14c --- /dev/null +++ b/content/10.quick-code/4.upgrading.md @@ -0,0 +1,155 @@ +--- +title: Upgradeability +description: Learn how to write and customize your documentation. +--- + +This is only a basic example of what you can achieve with [Nuxt UI Pro](https://ui.nuxt.com/pro/guide), you can tweak it +to match your needs. The template uses several Nuxt modules underneath like [`@nuxt/content`](https://content.nuxt.com) +for the content, [`@nuxtjs/fontaine`](https://github.com/nuxt-modules/fontaine) and +[`@nuxtjs/google-fonts`](https://github.com/nuxt-modules/google-fonts) to change the font and +[`nuxt-og-image`](https://nuxtseo.com/og-image/getting-started/installation) for social previews. + +## ::callout + +icon: i-heroicons-light-bulb target: \_blank to: [nuxt guide](https://ui.nuxt.com/pro/guide/usage#structure) + +--- + +Learn more on how to customize and structure a Nuxt UI Pro app! :: + +## Writing content + +You can just start writing `.md` or `.yml` files in the [`content/`](https://content.nuxt.com/usage/content-directory) +directory to have your pages updated. The navigation will be automatically generated in the left aside and in the mobile +menu. You will also be able to go through your content with full-text search. + +::callout{icon="i-heroicons-light-bulb"} This template relies on a +[catch-all route](https://nuxt.com/docs/guide/directory-structure/pages#catch-all-route) but you can achieve the same +thing with the [document-driven mode](https://content.nuxt.com/document-driven/introduction). :: + +## App Configuration + +In addition to `@nuxt/ui-pro` configuration through the `app.config.ts`, this template lets you customize the `Header`, +`Footer` and the `Table of contents` components. + +### Header + +```ts [app.config.ts] +export default defineAppConfig({ + header: { + // Logo configuration + logo: { + // Light mode + light: { + src: '', + alt: '', + class: '', + }, + // Dark mode + dark: { + src: '', + alt: '', + class: '', + }, + }, + // Show or hide the search bar + search: true, + // Show or hide the color mode button + colorMode: true, + // Customize links + links: [ + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt-ui-pro/docs', + target: '_blank', + 'aria-label': 'Docs template on GitHub', + }, + ], + }, +}); +``` + +### Footer + +```ts [app.config.ts] +export default defineAppConfig({ + footer: { + // Update bottom left credits + credits: 'Copyright © 2023', + // Show or hide the color mode button + colorMode: false, + // Customize links + links: [ + { + icon: 'i-simple-icons-nuxtdotjs', + to: 'https://nuxt.com', + target: '_blank', + 'aria-label': 'Nuxt Website', + }, + { + icon: 'i-simple-icons-discord', + to: 'https://discord.com/invite/ps2h6QT', + target: '_blank', + 'aria-label': 'Nuxt UI on Discord', + }, + { + icon: 'i-simple-icons-x', + to: 'https://x.com/nuxt_js', + target: '_blank', + 'aria-label': 'Nuxt on X', + }, + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt/ui', + target: '_blank', + 'aria-label': 'Nuxt UI on GitHub', + }, + ], + }, +}); +``` + +### Table of contents + +```ts [app.config.ts] +export default defineAppConfig({ + toc: { + // Title of the main table of contents + title: 'Table of Contents', + // Bottom TOC configuration + bottom: { + // Title of the bottom table of contents + title: 'Community', + // URL of your repository content folder + edit: '', + // Customize links + links: [ + { + icon: 'i-heroicons-star', + label: 'Star on GitHub', + to: 'https://github.com/nuxt/ui', + target: '_blank', + }, + { + icon: 'i-heroicons-book-open', + label: 'Nuxt UI Pro docs', + to: 'https://ui.nuxt.com/pro/guide', + target: '_blank', + }, + { + icon: 'i-simple-icons-nuxtdotjs', + label: 'Purchase a license', + to: 'https://ui.nuxt.com/pro/purchase', + target: '_blank', + }, + ], + }, + }, +}); +``` + +icon: i-heroicons-light-bulb target: \_blank to: [app-config](https://nuxt.studio/docs/developers/app-config) + +--- + +A dedicated interface is provided to edit those configurations on Nuxt Studio. :: diff --git a/content/10.quick-code/5.verifying.md b/content/10.quick-code/5.verifying.md new file mode 100644 index 00000000..ecc1e1c4 --- /dev/null +++ b/content/10.quick-code/5.verifying.md @@ -0,0 +1,155 @@ +--- +title: Verifying +description: Learn how to write and customize your documentation. +--- + +This is only a basic example of what you can achieve with [Nuxt UI Pro](https://ui.nuxt.com/pro/guide), you can tweak it +to match your needs. The template uses several Nuxt modules underneath like [`@nuxt/content`](https://content.nuxt.com) +for the content, [`@nuxtjs/fontaine`](https://github.com/nuxt-modules/fontaine) and +[`@nuxtjs/google-fonts`](https://github.com/nuxt-modules/google-fonts) to change the font and +[`nuxt-og-image`](https://nuxtseo.com/og-image/getting-started/installation) for social previews. + +## ::callout + +icon: i-heroicons-light-bulb target: \_blank to: [nuxt guide](https://ui.nuxt.com/pro/guide/usage#structure) + +--- + +Learn more on how to customize and structure a Nuxt UI Pro app! :: + +## Writing content + +You can just start writing `.md` or `.yml` files in the [`content/`](https://content.nuxt.com/usage/content-directory) +directory to have your pages updated. The navigation will be automatically generated in the left aside and in the mobile +menu. You will also be able to go through your content with full-text search. + +::callout{icon="i-heroicons-light-bulb"} This template relies on a +[catch-all route](https://nuxt.com/docs/guide/directory-structure/pages#catch-all-route) but you can achieve the same +thing with the [document-driven mode](https://content.nuxt.com/document-driven/introduction). :: + +## App Configuration + +In addition to `@nuxt/ui-pro` configuration through the `app.config.ts`, this template lets you customize the `Header`, +`Footer` and the `Table of contents` components. + +### Header + +```ts [app.config.ts] +export default defineAppConfig({ + header: { + // Logo configuration + logo: { + // Light mode + light: { + src: '', + alt: '', + class: '', + }, + // Dark mode + dark: { + src: '', + alt: '', + class: '', + }, + }, + // Show or hide the search bar + search: true, + // Show or hide the color mode button + colorMode: true, + // Customize links + links: [ + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt-ui-pro/docs', + target: '_blank', + 'aria-label': 'Docs template on GitHub', + }, + ], + }, +}); +``` + +### Footer + +```ts [app.config.ts] +export default defineAppConfig({ + footer: { + // Update bottom left credits + credits: 'Copyright © 2023', + // Show or hide the color mode button + colorMode: false, + // Customize links + links: [ + { + icon: 'i-simple-icons-nuxtdotjs', + to: 'https://nuxt.com', + target: '_blank', + 'aria-label': 'Nuxt Website', + }, + { + icon: 'i-simple-icons-discord', + to: 'https://discord.com/invite/ps2h6QT', + target: '_blank', + 'aria-label': 'Nuxt UI on Discord', + }, + { + icon: 'i-simple-icons-x', + to: 'https://x.com/nuxt_js', + target: '_blank', + 'aria-label': 'Nuxt on X', + }, + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt/ui', + target: '_blank', + 'aria-label': 'Nuxt UI on GitHub', + }, + ], + }, +}); +``` + +### Table of contents + +```ts [app.config.ts] +export default defineAppConfig({ + toc: { + // Title of the main table of contents + title: 'Table of Contents', + // Bottom TOC configuration + bottom: { + // Title of the bottom table of contents + title: 'Community', + // URL of your repository content folder + edit: '', + // Customize links + links: [ + { + icon: 'i-heroicons-star', + label: 'Star on GitHub', + to: 'https://github.com/nuxt/ui', + target: '_blank', + }, + { + icon: 'i-heroicons-book-open', + label: 'Nuxt UI Pro docs', + to: 'https://ui.nuxt.com/pro/guide', + target: '_blank', + }, + { + icon: 'i-simple-icons-nuxtdotjs', + label: 'Purchase a license', + to: 'https://ui.nuxt.com/pro/purchase', + target: '_blank', + }, + ], + }, + }, +}); +``` + +icon: i-heroicons-light-bulb target: \_blank to: [app-config](https://nuxt.studio/docs/developers/app-config) + +--- + +A dedicated interface is provided to edit those configurations on Nuxt Studio. :: diff --git a/content/10.quick-code/6.paymaster.md b/content/10.quick-code/6.paymaster.md new file mode 100644 index 00000000..8f8fbae5 --- /dev/null +++ b/content/10.quick-code/6.paymaster.md @@ -0,0 +1,155 @@ +--- +title: Paymaster +description: Learn how to write and customize your documentation. +--- + +This is only a basic example of what you can achieve with [Nuxt UI Pro](https://ui.nuxt.com/pro/guide), you can tweak it +to match your needs. The template uses several Nuxt modules underneath like [`@nuxt/content`](https://content.nuxt.com) +for the content, [`@nuxtjs/fontaine`](https://github.com/nuxt-modules/fontaine) and +[`@nuxtjs/google-fonts`](https://github.com/nuxt-modules/google-fonts) to change the font and +[`nuxt-og-image`](https://nuxtseo.com/og-image/getting-started/installation) for social previews. + +## ::callout + +icon: i-heroicons-light-bulb target: \_blank to: [nuxt guide](https://ui.nuxt.com/pro/guide/usage#structure) + +--- + +Learn more on how to customize and structure a Nuxt UI Pro app! :: + +## Writing content + +You can just start writing `.md` or `.yml` files in the [`content/`](https://content.nuxt.com/usage/content-directory) +directory to have your pages updated. The navigation will be automatically generated in the left aside and in the mobile +menu. You will also be able to go through your content with full-text search. + +::callout{icon="i-heroicons-light-bulb"} This template relies on a +[catch-all route](https://nuxt.com/docs/guide/directory-structure/pages#catch-all-route) but you can achieve the same +thing with the [document-driven mode](https://content.nuxt.com/document-driven/introduction). :: + +## App Configuration + +In addition to `@nuxt/ui-pro` configuration through the `app.config.ts`, this template lets you customize the `Header`, +`Footer` and the `Table of contents` components. + +### Header + +```ts [app.config.ts] +export default defineAppConfig({ + header: { + // Logo configuration + logo: { + // Light mode + light: { + src: '', + alt: '', + class: '', + }, + // Dark mode + dark: { + src: '', + alt: '', + class: '', + }, + }, + // Show or hide the search bar + search: true, + // Show or hide the color mode button + colorMode: true, + // Customize links + links: [ + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt-ui-pro/docs', + target: '_blank', + 'aria-label': 'Docs template on GitHub', + }, + ], + }, +}); +``` + +### Footer + +```ts [app.config.ts] +export default defineAppConfig({ + footer: { + // Update bottom left credits + credits: 'Copyright © 2023', + // Show or hide the color mode button + colorMode: false, + // Customize links + links: [ + { + icon: 'i-simple-icons-nuxtdotjs', + to: 'https://nuxt.com', + target: '_blank', + 'aria-label': 'Nuxt Website', + }, + { + icon: 'i-simple-icons-discord', + to: 'https://discord.com/invite/ps2h6QT', + target: '_blank', + 'aria-label': 'Nuxt UI on Discord', + }, + { + icon: 'i-simple-icons-x', + to: 'https://x.com/nuxt_js', + target: '_blank', + 'aria-label': 'Nuxt on X', + }, + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt/ui', + target: '_blank', + 'aria-label': 'Nuxt UI on GitHub', + }, + ], + }, +}); +``` + +### Table of contents + +```ts [app.config.ts] +export default defineAppConfig({ + toc: { + // Title of the main table of contents + title: 'Table of Contents', + // Bottom TOC configuration + bottom: { + // Title of the bottom table of contents + title: 'Community', + // URL of your repository content folder + edit: '', + // Customize links + links: [ + { + icon: 'i-heroicons-star', + label: 'Star on GitHub', + to: 'https://github.com/nuxt/ui', + target: '_blank', + }, + { + icon: 'i-heroicons-book-open', + label: 'Nuxt UI Pro docs', + to: 'https://ui.nuxt.com/pro/guide', + target: '_blank', + }, + { + icon: 'i-simple-icons-nuxtdotjs', + label: 'Purchase a license', + to: 'https://ui.nuxt.com/pro/purchase', + target: '_blank', + }, + ], + }, + }, +}); +``` + +icon: i-heroicons-light-bulb target: \_blank to: [app-config](https://nuxt.studio/docs/developers/app-config) + +--- + +A dedicated interface is provided to edit those configurations on Nuxt Studio. :: diff --git a/content/10.quick-code/7.smart-account.md b/content/10.quick-code/7.smart-account.md new file mode 100644 index 00000000..ff479b8a --- /dev/null +++ b/content/10.quick-code/7.smart-account.md @@ -0,0 +1,155 @@ +--- +title: Smart Account +description: Learn how to write and customize your documentation. +--- + +This is only a basic example of what you can achieve with [Nuxt UI Pro](https://ui.nuxt.com/pro/guide), you can tweak it +to match your needs. The template uses several Nuxt modules underneath like [`@nuxt/content`](https://content.nuxt.com) +for the content, [`@nuxtjs/fontaine`](https://github.com/nuxt-modules/fontaine) and +[`@nuxtjs/google-fonts`](https://github.com/nuxt-modules/google-fonts) to change the font and +[`nuxt-og-image`](https://nuxtseo.com/og-image/getting-started/installation) for social previews. + +## ::callout + +icon: i-heroicons-light-bulb target: \_blank to: [nuxt guide](https://ui.nuxt.com/pro/guide/usage#structure) + +--- + +Learn more on how to customize and structure a Nuxt UI Pro app! :: + +## Writing content + +You can just start writing `.md` or `.yml` files in the [`content/`](https://content.nuxt.com/usage/content-directory) +directory to have your pages updated. The navigation will be automatically generated in the left aside and in the mobile +menu. You will also be able to go through your content with full-text search. + +::callout{icon="i-heroicons-light-bulb"} This template relies on a +[catch-all route](https://nuxt.com/docs/guide/directory-structure/pages#catch-all-route) but you can achieve the same +thing with the [document-driven mode](https://content.nuxt.com/document-driven/introduction). :: + +## App Configuration + +In addition to `@nuxt/ui-pro` configuration through the `app.config.ts`, this template lets you customize the `Header`, +`Footer` and the `Table of contents` components. + +### Header + +```ts [app.config.ts] +export default defineAppConfig({ + header: { + // Logo configuration + logo: { + // Light mode + light: { + src: '', + alt: '', + class: '', + }, + // Dark mode + dark: { + src: '', + alt: '', + class: '', + }, + }, + // Show or hide the search bar + search: true, + // Show or hide the color mode button + colorMode: true, + // Customize links + links: [ + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt-ui-pro/docs', + target: '_blank', + 'aria-label': 'Docs template on GitHub', + }, + ], + }, +}); +``` + +### Footer + +```ts [app.config.ts] +export default defineAppConfig({ + footer: { + // Update bottom left credits + credits: 'Copyright © 2023', + // Show or hide the color mode button + colorMode: false, + // Customize links + links: [ + { + icon: 'i-simple-icons-nuxtdotjs', + to: 'https://nuxt.com', + target: '_blank', + 'aria-label': 'Nuxt Website', + }, + { + icon: 'i-simple-icons-discord', + to: 'https://discord.com/invite/ps2h6QT', + target: '_blank', + 'aria-label': 'Nuxt UI on Discord', + }, + { + icon: 'i-simple-icons-x', + to: 'https://x.com/nuxt_js', + target: '_blank', + 'aria-label': 'Nuxt on X', + }, + { + icon: 'i-simple-icons-github', + to: 'https://github.com/nuxt/ui', + target: '_blank', + 'aria-label': 'Nuxt UI on GitHub', + }, + ], + }, +}); +``` + +### Table of contents + +```ts [app.config.ts] +export default defineAppConfig({ + toc: { + // Title of the main table of contents + title: 'Table of Contents', + // Bottom TOC configuration + bottom: { + // Title of the bottom table of contents + title: 'Community', + // URL of your repository content folder + edit: '', + // Customize links + links: [ + { + icon: 'i-heroicons-star', + label: 'Star on GitHub', + to: 'https://github.com/nuxt/ui', + target: '_blank', + }, + { + icon: 'i-heroicons-book-open', + label: 'Nuxt UI Pro docs', + to: 'https://ui.nuxt.com/pro/guide', + target: '_blank', + }, + { + icon: 'i-simple-icons-nuxtdotjs', + label: 'Purchase a license', + to: 'https://ui.nuxt.com/pro/purchase', + target: '_blank', + }, + ], + }, + }, +}); +``` + +icon: i-heroicons-light-bulb target: \_blank to: [app-config](https://nuxt.studio/docs/developers/app-config) + +--- + +A dedicated interface is provided to edit those configurations on Nuxt Studio. :: diff --git a/content/10.quick-code/_dir.yml b/content/10.quick-code/_dir.yml new file mode 100644 index 00000000..46117271 --- /dev/null +++ b/content/10.quick-code/_dir.yml @@ -0,0 +1,2 @@ +title: QuickCode +layout: docs diff --git a/content/10.getting-started/_getting-started/_foundry_deploy_contract.md b/content/10.quick-code/_quick-code/_foundry_deploy_contract.md similarity index 100% rename from content/10.getting-started/_getting-started/_foundry_deploy_contract.md rename to content/10.quick-code/_quick-code/_foundry_deploy_contract.md diff --git a/content/10.getting-started/_getting-started/_hardhat_deploy_contract.md b/content/10.quick-code/_quick-code/_hardhat_deploy_contract.md similarity index 100% rename from content/10.getting-started/_getting-started/_hardhat_deploy_contract.md rename to content/10.quick-code/_quick-code/_hardhat_deploy_contract.md From f2b794aeacf5ef262cf875cf73ef3300dfacc7f7 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Tue, 2 Apr 2024 08:27:21 -0500 Subject: [PATCH 04/54] fix: update nav link --- content/index.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/index.yml b/content/index.yml index a86206b1..874ef19b 100644 --- a/content/index.yml +++ b/content/index.yml @@ -15,7 +15,7 @@ hero: - label: Start building on zkSync icon: i-heroicons-arrow-right-20-solid trailing: true - to: '/getting-started' + to: '/quick-code' size: lg # - label: Use this template # icon: i-simple-icons-github From eaa957e8574fb1e8c1f9abf7f581051f2708cfd7 Mon Sep 17 00:00:00 2001 From: Sabrina Ferguson Date: Wed, 3 Apr 2024 11:57:05 -0400 Subject: [PATCH 05/54] feat: create components for markdown content --- .markdownlint.json | 3 - .prettierignore | 1 + .vscode/settings.json | 7 +- bun.lockb | Bin 615027 -> 642553 bytes components/content/CheckIcon.vue | 17 ++++ components/content/DeployContract.vue | 43 --------- components/content/DropPanel/DropPanel.vue | 51 +++++++++++ components/content/DropPanel/Panel.vue | 11 +++ components/content/EnvSetup.vue | 51 ----------- components/content/IconList.vue | 46 ---------- components/content/WalletSetup.vue | 43 --------- content/10.quick-code/1.index.md | 21 +++-- content/10.quick-code/2.deploy-factory.md | 1 - content/10.quick-code/4.upgrading.md | 2 +- .../_index/_foundry_deploy_contract.md} | 28 ++++-- .../_index/_hardhat_deploy_contract.md} | 39 ++++++-- .../_quick-code/_foundry_deploy_contract.md | 26 ------ .../_quick-code/_hardhat_deploy_contract.md | 26 ------ content/_partials/deploy-contract.md | 19 ++++ .../environment-setup-with-zksync-cli.md | 47 ++++++++++ content/_partials/setting-up-your-wallet.md | 86 ++++++++---------- nuxt.config.ts | 2 +- package.json | 3 + 23 files changed, 254 insertions(+), 319 deletions(-) create mode 100644 components/content/CheckIcon.vue delete mode 100644 components/content/DeployContract.vue create mode 100644 components/content/DropPanel/DropPanel.vue create mode 100644 components/content/DropPanel/Panel.vue delete mode 100644 components/content/EnvSetup.vue delete mode 100644 components/content/IconList.vue delete mode 100644 components/content/WalletSetup.vue rename content/{_partials/deploy_contract_foundry.md => 10.quick-code/_index/_foundry_deploy_contract.md} (68%) rename content/{_partials/deploy_contract_hardhat.md => 10.quick-code/_index/_hardhat_deploy_contract.md} (69%) delete mode 100644 content/10.quick-code/_quick-code/_foundry_deploy_contract.md delete mode 100644 content/10.quick-code/_quick-code/_hardhat_deploy_contract.md create mode 100644 content/_partials/deploy-contract.md create mode 100644 content/_partials/environment-setup-with-zksync-cli.md diff --git a/.markdownlint.json b/.markdownlint.json index 123df027..4ea32266 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -2,9 +2,6 @@ "$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint/main/schema/markdownlint-config-schema.json", "default": true, "MD001": false, - "MD003": { - "style": "atx" - }, "MD007": { "indent": 2 }, diff --git a/.prettierignore b/.prettierignore index 3f5a5547..d9130750 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,4 @@ node_modules .idea public **/*.md +*.md diff --git a/.vscode/settings.json b/.vscode/settings.json index e61fdac9..6d2cb901 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,9 +8,8 @@ "editor.insertSpaces": true, "files.eol": "\n", "[markdown]": { - "editor.wordWrap": "on" - }, - "editor.codeActionsOnSave": { - "source.fixAll.markdownlint": true + "editor.wordWrap": "on", + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint", + "editor.formatOnSave": true } } diff --git a/bun.lockb b/bun.lockb index 36a4beac67e6e78dc88d09f41bc7c1c683bf9332..43774e2d20277bba2c7b73fe7bbba65307e5e740 100755 GIT binary patch delta 127763 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 z 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 + + + + diff --git a/components/content/DeployContract.vue b/components/content/DeployContract.vue deleted file mode 100644 index 136b5d6d..00000000 --- a/components/content/DeployContract.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/components/content/DropPanel/DropPanel.vue b/components/content/DropPanel/DropPanel.vue new file mode 100644 index 00000000..bed9f25d --- /dev/null +++ b/components/content/DropPanel/DropPanel.vue @@ -0,0 +1,51 @@ + + + diff --git a/components/content/DropPanel/Panel.vue b/components/content/DropPanel/Panel.vue new file mode 100644 index 00000000..a0aa04e7 --- /dev/null +++ b/components/content/DropPanel/Panel.vue @@ -0,0 +1,11 @@ + + + diff --git a/components/content/EnvSetup.vue b/components/content/EnvSetup.vue deleted file mode 100644 index 9bd6db3f..00000000 --- a/components/content/EnvSetup.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - diff --git a/components/content/IconList.vue b/components/content/IconList.vue deleted file mode 100644 index ad555d74..00000000 --- a/components/content/IconList.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/components/content/WalletSetup.vue b/components/content/WalletSetup.vue deleted file mode 100644 index 7ff271f7..00000000 --- a/components/content/WalletSetup.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/content/10.quick-code/1.index.md b/content/10.quick-code/1.index.md index 62229116..227bd477 100644 --- a/content/10.quick-code/1.index.md +++ b/content/10.quick-code/1.index.md @@ -1,15 +1,16 @@ --- title: Smart Contracts description: Learn to deploy smart contracts efficiently in the zkSync environment. -layout: test --- -Welcome to the Quickstart Guide for deploying smart contracts on zkSync! In this guide, we'll walk you through the process of creating and deploying a simple smart contract that holds an adventure for Zeek. +Welcome to the Quickstart Guide for deploying smart contracts on zkSync! In this guide, we'll walk you through the process +of creating and deploying a simple smart contract that holds an adventure for Zeek. -::icon-list ---- ---- -:: +:check-icon Initializing a new project with a scaffolding tool. + +:check-icon Crafting a smart contract to store Zeek's latest adventure. + +:check-icon Deploying the contract on the zkSync Era using your choice of Hardhat or Foundry. Let's dive in and start your developer journey on zkSync! @@ -21,21 +22,21 @@ Select the framework you want to get started using zkSync Era with. --- items: [{ label: 'HardHat', - partial: '_quick-code/_hardhat_deploy_contract' + partial: '_index/_hardhat_deploy_contract' }, { label: 'Foundry', - partial: '_quick-code/_foundry_deploy_contract' + partial: '_index/_foundry_deploy_contract' }] --- :: - ## Takeaways - zkSync is EVM compatible and you can write smart contracts in Solidity or Vyper. - zkSync supports your favorite development toolkit Hardhat and Foundry. - zkSync CLI provides a quick way to scaffold different types of projects thanks to its multiple templates. -- Contracts deployed to zkSync are compiled using `zksolc` or `zkvyper` as they generate a special bytecode for zkSync's ZKEVM. +- Contracts deployed to zkSync are compiled using `zksolc` or `zkvyper` as they generate a special bytecode +for zkSync's ZKEVM. ## Next steps diff --git a/content/10.quick-code/2.deploy-factory.md b/content/10.quick-code/2.deploy-factory.md index 7d4d1eb5..995b412a 100644 --- a/content/10.quick-code/2.deploy-factory.md +++ b/content/10.quick-code/2.deploy-factory.md @@ -1,7 +1,6 @@ --- title: Contract Factories description: Get started with Nuxt UI Pro documentation template. -layout: docs --- ::callout{icon="i-heroicons-light-bulb"} diff --git a/content/10.quick-code/4.upgrading.md b/content/10.quick-code/4.upgrading.md index 5999d14c..8c168d38 100644 --- a/content/10.quick-code/4.upgrading.md +++ b/content/10.quick-code/4.upgrading.md @@ -9,7 +9,7 @@ for the content, [`@nuxtjs/fontaine`](https://github.com/nuxt-modules/fontaine) [`@nuxtjs/google-fonts`](https://github.com/nuxt-modules/google-fonts) to change the font and [`nuxt-og-image`](https://nuxtseo.com/og-image/getting-started/installation) for social previews. -## ::callout +::callout icon: i-heroicons-light-bulb target: \_blank to: [nuxt guide](https://ui.nuxt.com/pro/guide/usage#structure) diff --git a/content/_partials/deploy_contract_foundry.md b/content/10.quick-code/_index/_foundry_deploy_contract.md similarity index 68% rename from content/_partials/deploy_contract_foundry.md rename to content/10.quick-code/_index/_foundry_deploy_contract.md index c84a71a9..557e6104 100644 --- a/content/_partials/deploy_contract_foundry.md +++ b/content/10.quick-code/_index/_foundry_deploy_contract.md @@ -1,8 +1,20 @@ --- -title: Deploy contract using Foundry +title: Foundry | Deploy Contract --- -Now that we have setup our environment and our wallet, we are ready to deploy our first contract! For this tutorial we'll focus on the `/src/ZeekAdventures.sol` contract: +`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. +It extends Foundry's capabilities for Ethereum app development to support zkSync, +allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. + +## Step 1: Setting up environment (todo - update) + +## Step 2: Set up wallet + +## Step 3: Deploying your first contract + +Now that we have setup our environment and our wallet, +we are ready to deploy our first contract! +For this tutorial we'll focus on the `/src/ZeekAdventures.sol` contract: ```solidity // SPDX-License-Identifier: MIT @@ -66,13 +78,15 @@ The compiled artifacts will be located in the `/zkout` folder. ### Deploy -In this section, we’ll get to deploy the ZeekAdventure contract onto {network}. The script to deploy contracts is found at `/scripts/deploy.s.sol`. +In this section, we’ll get to deploy the ZeekAdventure contract +onto {network}. +The script to deploy contracts is found at `/scripts/deploy.s.sol`. // TODO: -@Dustin -Write foundry script for deployment. Add and explain here. +@Dustin +Write foundry script for deployment. Add and explain here. -To deploy the contract, run the following command: +To deploy the contract, run the following command: ```bash forge script command --zksync @@ -84,4 +98,4 @@ forge script command --zksync TODO ``` -🥳 Congratulations! You just deployed a smart contract to {network}! \ No newline at end of file +🥳 Congratulations! You just deployed a smart contract to {network}! diff --git a/content/_partials/deploy_contract_hardhat.md b/content/10.quick-code/_index/_hardhat_deploy_contract.md similarity index 69% rename from content/_partials/deploy_contract_hardhat.md rename to content/10.quick-code/_index/_hardhat_deploy_contract.md index a63d06c2..11a2ed0d 100644 --- a/content/_partials/deploy_contract_hardhat.md +++ b/content/10.quick-code/_index/_hardhat_deploy_contract.md @@ -1,8 +1,27 @@ --- -title: Deploy contract using Hardhat +title: Hardhat | Deploy Contract --- -Now that we have setup our environment and our wallet, we are ready to deploy our first contract! For this tutorial we'll focus on the `/contracts/ZeekAdventures.sol` contract: +Hardhat is an Ethereum development environment, designed for easy smart contract development in Solidity. +zkSync provides its own plugins which makes working with contracts on zkSync simple and efficient. + +## Step 1: Setting up environment +:display-partial{partial = "Environment Setup with zkSync CLI"} + +## Step 2: Set up wallet + +Deploying contracts on the zkSync Sepolia testnet requires having testnet ETH. +If you're working within the local development environment, +you can utilize pre-configured rich wallets and skip this step. +For testnet deployments, follow these steps to secure your funds: + +:display-partial{partial = "Setting up your wallet"} + +## Step 3: Deploying your first contract +:display-partial{partial = "Deploy Contract"} + +Now that we have setup our environment and our wallet, we are ready to deploy our first contract! +For this tutorial we'll focus on the `/contracts/ZeekAdventures.sol` contract: ```solidity // SPDX-License-Identifier: MIT @@ -50,7 +69,8 @@ contract ZeekAdventures { ### Compile contract -Smart contracts deployed to zkSync must be compiled using our custom compilers. For this particular guide we are making use of `zksolc`. +Smart contracts deployed to zkSync must be compiled using our custom compilers. +For this particular guide we are making use of `zksolc`. To compile the contracts in the project, run the following command: @@ -71,6 +91,7 @@ npm run compile:contracts ```bash [bun] bun run compile:contracts ``` + :: **You'll get the following output:** @@ -86,11 +107,14 @@ The compiled artifacts will be located in the `/artifacts-zk` folder. ### Deploy -In this section, we’ll get to deploy the ZeekAdventure contract onto {network}. The script to deploy contracts is found at `/deploy/deploy.ts`. +In this section, we’ll get to deploy the ZeekAdventure contract +onto {network}. +The script to deploy contracts is found at `/deploy/deploy.ts`. -The `contractArtifactName` defines the contract we want to deploy.Here we have it set to our contract, ZeekAdventure. Similarly, `constructorArguments` are the arguments we need to provide to the constructor to initialize the contract. +The `contractArtifactName` defines the contract we want to deploy.Here we have it set to our contract, ZeekAdventure. +Similarly, `constructorArguments` are the arguments we need to provide to the constructor to initialize the contract. -To deploy the contract, run the following command: +To deploy the contract, run the following command: ::code-group @@ -109,6 +133,7 @@ npm run deploy ```bash [bun] bun run deploy ``` + :: **You'll get the following output:** @@ -123,4 +148,4 @@ Estimated deployment cost: 0.0000648863 ETH - Encoded constructor arguments: 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000 ``` -🥳 Congratulations! You just deployed a smart contract to {network}! \ No newline at end of file +🥳 Congratulations! You just deployed a smart contract to {network}! diff --git a/content/10.quick-code/_quick-code/_foundry_deploy_contract.md b/content/10.quick-code/_quick-code/_foundry_deploy_contract.md deleted file mode 100644 index 159b16d7..00000000 --- a/content/10.quick-code/_quick-code/_foundry_deploy_contract.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Foundry | Deploy Contract ---- - -`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. It extends Foundry's capabilities for Ethereum app development to support zkSync, allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. - -## Step 1: Setting up environment (todo - update) -::env-setup ---- - ---- -:: - -## Step 2: Set up wallet -::wallet-setup ---- - ---- -:: - -## Step 3: Deploying your first contract -::display-partial ---- -partial: 'Deploy contract using Foundry' ---- -:: diff --git a/content/10.quick-code/_quick-code/_hardhat_deploy_contract.md b/content/10.quick-code/_quick-code/_hardhat_deploy_contract.md deleted file mode 100644 index 7f0644dd..00000000 --- a/content/10.quick-code/_quick-code/_hardhat_deploy_contract.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Hardhat | Deploy Contract ---- - -Hardhat is an Ethereum development environment, designed for easy smart contract development in Solidity. zkSync provides its own plugins which makes working with contracts on zkSync simple and efficient. - -## Step 1: Setting up environment -::env-setup ---- - ---- -:: - -## Step 2: Set up wallet -::wallet-setup ---- - ---- -:: - -## Step 3: Deploying your first contract -::display-partial ---- -partial: 'Deploy contract using Hardhat' ---- -:: diff --git a/content/_partials/deploy-contract.md b/content/_partials/deploy-contract.md new file mode 100644 index 00000000..7bea2ee7 --- /dev/null +++ b/content/_partials/deploy-contract.md @@ -0,0 +1,19 @@ +--- +title: Deploy Contract +--- + +::drop-panel + ::panel{label = "Deploy Contract"} + Deploying contracts on the zkSync Sepolia testnet requires having testnet ETH. + If you're working within the local development environment, + you can utilize pre-configured rich wallets and skip this step. + For testnet deployments, follow these steps to secure your funds: + + 1. Obtaining Testnet ETH: + - Use the [LearnWeb3 faucet](https://learnweb3.io/faucets/zksync_sepolia/) to directly receive testnet ETH on zkSync Sepolia. + - Alternatively, acquire SepoliaETH from [recommended faucets](https://www.notion.so/tooling/network-faucets.md) and transfer it to the zkSync Sepolia testnet via the [zkSync bridge](https://portal.zksync.io/bridge/?network=sepolia). + + 1. Verify your balance: + - Check your wallet's balance using the [zkSync Sepolia explorer](https://sepolia.explorer.zksync.io/). + :: +:: diff --git a/content/_partials/environment-setup-with-zksync-cli.md b/content/_partials/environment-setup-with-zksync-cli.md new file mode 100644 index 00000000..8d313f52 --- /dev/null +++ b/content/_partials/environment-setup-with-zksync-cli.md @@ -0,0 +1,47 @@ +--- +title: Environment Setup With zkSync CLI +--- + +::drop-panel + ::panel{label="Install Node.js or Bun.sh"} + To effectively utilize the **zksync-cli** tool, your + development environment needs to be set up with either Node.js or Bun.sh. + The choice depends on your project's requirements and personal preference for package management and execution speed. + + - Node.js + - Download the Long-Term Support (LTS) version from the [official Node.js website](https://nodejs.org/en/download). + - For first-time users, the [Node.js usage guide](https://nodejs.org/api/synopsis.html#usage) offers comprehensive instructions on getting started. + - Bun.sh + - Obtain the latest version from the [Bun installation page](https://bun.sh/docs/installation). Bun.sh is known for its high performance and modern JavaScript features. + :: + + ::panel{label="Setup local node (optional)"} + After installing Node.js or Bun, leveraging **zksync-cli** to set up + a local development environment is a beneficial next step. + This local setup allows for quicker testing and debugging + processes without incurring testnet transaction costs. + + **Prerequisites:** + + **Docker Installation:** **zksync-cli**'s local environment relies on Docker. + Download the appropriate version from the Docker website. + If you're new to Docker, consider exploring the Docker getting started guide. + + 1. **Open Your Terminal:** Access it from the Applications folder or use Spotlight search (Command + Space). + 2. **Initialize Local zkSync Node:** + - Run the command: **npx zksync-cli@latest dev start** + - When prompted, choose “In memory node” to deploy a local zkSync node in a Docker container. + 3. **Access Rich Accounts:** For pre-configured rich wallets, visit [era-test-node rich wallets](https://era.zksync.io/docs/tools/testing/era-test-node.html#use-pre-configured-rich-wallets). + 4. **Local Node URL:** Your local zkSync node is accessible at + **[http://127.0.0.1:8011](http://127.0.0.1:8011/)**, ready for deployment or testing purposes. + :: + + ::panel{label="Initialize project"} + Run the following command in your terminal to create a new project using zkSync CLI. + + ```sh + npx zksync-cli@latest —template zksync-quickstart + ``` + + :: +:: diff --git a/content/_partials/setting-up-your-wallet.md b/content/_partials/setting-up-your-wallet.md index c1070e6d..7130f6c8 100644 --- a/content/_partials/setting-up-your-wallet.md +++ b/content/_partials/setting-up-your-wallet.md @@ -2,53 +2,39 @@ title: Setting up your wallet --- - \ No newline at end of file +::drop-panel + ::panel{label="Fund Wallet"} + + 1. Obtaining Testnet ETH: + + - Use the [LearnWeb3 faucet](https://learnweb3.io/faucets/zksync_sepolia/) + to directly receive testnet ETH on zkSync Sepolia. + - Alternatively, acquire SepoliaETH from [recommended faucets](https://www.notion.so/tooling/network-faucets.md) and + transfer it to the zkSync Sepolia testnet via the [zkSync bridge](https://portal.zksync.io/bridge/?network=sepolia). + + 1. Verify your balance: + + - Check your wallet's balance using the zkSync Sepolia explorer. + :: + + ::panel{label="Configuring Your Wallet in the Project"} + To deploy contracts, you'll need to securely add your wallet's private key to the project environment: + + 1. **Extract Your Private Key:** + - Find your wallet's private key. For MetaMask users, here's how to + [export your wallet's private key](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key). + If you're in the local environment, use a private key from the available rich accounts. + + 1. **Prepare the Environment File:** + - Locate the **`.env-example`** file in your project directory. Rename this file to **`.env`**. + + 1. **Add Your Private Key:** + - Open the `.env` file and add your private key in the following format: + + ```sh + PRIVATE_KEY=your_private_key_here + ``` + + - Replace **`your_private_key_here`** with your actual private key. + :: +:: diff --git a/nuxt.config.ts b/nuxt.config.ts index 1700f8e6..9b9fb753 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -3,7 +3,7 @@ import { zksyncIcons } from './assets/zksync-icons'; // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ extends: ['@nuxt/ui-pro'], - modules: ['@nuxt/content', '@nuxt/ui', '@nuxt/fonts', '@nuxthq/studio', 'nuxt-og-image'], + modules: ['@nuxt/content', '@nuxt/ui', '@nuxt/fonts', '@nuxthq/studio', 'nuxt-og-image', 'nuxt-headlessui'], hooks: { // Define `@nuxt/ui` components as global to use them in `.md` (feel free to add those you need) 'components:extend': (components) => { diff --git a/package.json b/package.json index 015ae393..900dc939 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "husky": "^9.0.11", "markdownlint": "^0.33.0", "markdownlint-cli2": "^0.12.1", + "nuxt-headlessui": "latest", "particles.vue3": "^2.12.0", "prettier": "^3.1.0", "prettier-eslint": "^16.1.2", @@ -40,6 +41,8 @@ "@nuxt/ui-pro": "^1.0.2", "@tsparticles/slim": "^3.3.0", "@tsparticles/vue3": "^3.0.1", + "install": "^0.13.0", + "npm": "^10.5.0", "nuxt": "^3.10.3", "nuxt-og-image": "^3.0.0-rc.45" }, From c2383603077fb45c081cb65a8a3bbfca57ecca9e Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Fri, 5 Apr 2024 15:12:46 -0500 Subject: [PATCH 06/54] chore: Foundry content, and minor improvements --- app.config.ts | 36 ++-- bun.lockb | Bin 642553 -> 641937 bytes content/10.quick-code/1.index.md | 30 ++- .../_index/_foundry_deploy_contract.md | 196 +++++++++++++----- .../_index/_hardhat_deploy_contract.md | 176 ++++++++++------ .../environment-setup-with-foundry.md | 43 ++++ content/_partials/setting-up-your-wallet.md | 2 +- 7 files changed, 340 insertions(+), 143 deletions(-) create mode 100644 content/_partials/environment-setup-with-foundry.md diff --git a/app.config.ts b/app.config.ts index efab2003..8aabfbb7 100644 --- a/app.config.ts +++ b/app.config.ts @@ -94,24 +94,24 @@ export default defineAppConfig({ title: 'Community', edit: 'https://github.com/matter-labs/zksync-docs/edit/main/content', links: [ - // { - // icon: 'i-heroicons-star', - // label: 'Star on GitHub', - // to: 'https://github.com/nuxt/ui', - // target: '_blank', - // }, - // { - // icon: 'i-heroicons-book-open', - // label: 'Nuxt UI Pro docs', - // to: 'https://ui.nuxt.com/pro/guide', - // target: '_blank', - // }, - // { - // icon: 'i-simple-icons-nuxtdotjs', - // label: 'Purchase a license', - // to: 'https://ui.nuxt.com/pro/purchase', - // target: '_blank', - // }, + { + icon: 'i-heroicons-star', + label: 'Star on GitHub', + to: 'https://github.com/matter-labs/zksync-docs', + target: '_blank', + }, + { + icon: 'i-heroicons-chat-bubble-oval-left-ellipsis-16-solid', + label: 'Chat on Discord', + to: 'https://join.zksync.dev/', + target: '_blank', + }, + { + icon: 'i-heroicons-user-group-20-solid', + label: 'Developer Forum', + to: 'https://github.com/zkSync-Community-Hub/zkync-developers/discussions', + target: '_blank', + }, ], }, }, diff --git a/bun.lockb b/bun.lockb index 43774e2d20277bba2c7b73fe7bbba65307e5e740..74387b20e8d808f078319e1444c057a9f13da4ec 100755 GIT binary patch delta 15241 zcmXxo3A|(D{m1b;W1JaEsFJoyC>q8RT10KJl%R+`_S71Cs3j(CrMgI4ZPThYjP{@Yf6w>x>v_G-eLv5~bF!SAleyQTo7TK)X3Z;8tKEP4 zhq?DofB2Zo?kqk~-gatcgZH+1>Vx}sJN)qTdaoTj_qExxet6#Q7wtal@!e-{zr(Cq zGxZ&2?`w=8yQ5|e7(;F+#U@N3|8>O{L_2F%fHuT-Q7l3S;=3xApbLqdVi|gn+)c3p zeMs%DScL(k_fV|C5Hfoz)?ozMy%ZZThTPtYO_)G_AH^0#dCdyYhSq;Dk@fC0O_L?YcPb&(Ta5#LG~EM285*XhZA_#UgYdex_mxx{&ymVi|gnJWH_xeMp_HScL(k z&rz(w5Hc0TI*cGYt=NDuQoD?DwjjD(vjVgsR#PlO2jW*K zmY@rXD;3MogXC3;73f3iYQ-uHAbpKu4Tg}pRGDs=tANq#WM6D`5nay^dWV#Vig9EZYb7Z2$@?H>o9`st%?m8L+&=k zCQKlIyJ8EXJ2Wdm8)A1V7NG<2yA(^%g+x=a3_VERtyqCRr0!9y!T{3uD%M~Knfny$ zFoNv;iVYY;?g7OnOd$WDVhf^{W(8>bRhn)VhOsCcto)bJxD&PSb;vIzN=V; z0i+*Oticd6k1N(;1lhJ?1ICbBsMv%FiK3?cKpVjV`1eL=ASW5~Uz*n|n>Us7yA^gYcA(1zIe6^qb; zcvrCmT}b>uu?#&(zN}b*KBQhztik}&uPWAH2$>%$)?ozM*AyEthTQ9lO_)Hwr`Uq% zN17F&4Y4;Ai_n4ij}=SMg~XeRW#~cjEyW7-A@#Om6$X(0iDC_gkm)PdVFcNqDmGvY zxt}REVFLM|E4Co|g=Ph4L+qD|Md(2MSBfR*LgF38GV~xhP^>^7QomNL!T{3mD%M~K zncpbZVFcOVDmGvYx!)-^VFLNzE4CndPqPBFAvRPjLI>i1P%J?g5`R=ILl2UFQmjB9 zQtvBPVF2krE7o8LnZGF3VFcN~DmGvYxshTMCXoM|Vhf_bYgT|Z#6D0gLI>jiP%J?g z5+5p-p$Ex-DpsHmsedU}VF2l|Vhx6n`AD%2BglTN*nlzQ7AZDi0{KrATM+$QvjVgs z_8-L}bRhnJiY4emVxm}v9wh&(Sb@H{F148bwR&AuKL_FGOnR26!4NXD73(m9>|%-y z7(;H3ViP8iUtF;TQKVS`+7O$oScDG5=P8z;3yCEZ%g}@5l8P1RL+VqCRTw~eDa9HL zA+xk%9Y&CiDK=mXxn&faFoFECiY^7 zQgOv93?Thk#TpDD^Et&jj3E1Y#RiNax1wSbCXoMvVhf^`G%G+GVk;{ap#$-RVhOsC zSVgf6JxH#qSb;vIzNlD*0i;(`ticd6t1H%F1lctd8!(34mlT^YfqYW21<{(C6`&2V zwG@lcf%tsI5_BQ4wqhB2kX%Qx0)0rWt5}5rq}Nld!4NXKPiN{dg6#TQ*?=+RHc)KB z1oB^2Y(exD%?i+l*oKNl=s@tYcPb&riyhK zL3T66288~l)UcTj9Ww4-JPXhUo##UgYd{&mF?bRn^`Vi|gn z+(oeheMs%9ScL(kbBZ+>LS{F`I*cH@yJ7>zklRDC2@}Zgsn~*OFU<IuJiZu>@U69I9A`9wZM_tUw=9hbvZL0O=zXYcPb&k&1N~LAI#afHCBb zQf$Hm@<%JSAUZ~~0<i#VQOSeWGFwhLAZ) zu?{21ep9glW5}JX*n|n>Pf=_^bgE_rXhZBY#UgYdUREqY7ZRr{mZ1m9GZZV(ht!#h zRTx0}TZ%OpLgp;RI*cHDwqgUukUK}Q2@}Xy6k8BYYgT|Z#LiVLLI>jKDVCrMiSrf9 z(1YX!iWTTX>O#dT3?O}xVhx6nsVdfC1la|O4H!f2+loz?K>lLI7DSh5R)99dE>$c- z2jZ70mY@rX%N5JegJeyy0)0qbp;(0hq_0%0!4NW6Db`^G*{c;BFoxVUicOe6{#wNr zMAvCnfHuVHibd!^d`7VZT}WK7ScV=XZ&0j2A5u3eR$&0?n-ps>gv@so>o9`s&58{e zL$0CNgbCztQEWkUt7ZjgL+m!iB6J{ryJ88tkhnv!3_VERsaSzNr0!Cz!T{1u#TpDD zbGKq0Mv%Qnu>oVq-K*Gy3FPlnY(aFtW(8;c6hbRhnqVhOsCXepMV2g!#NE6|73 z!-`cHK>88I8Vn)xsA3&Pko~S=1ICbhOtA?Q$Um;wf~c)o0oo8-s91y!#Gg247ukNn=pa=^NKBqUeK%nZHT?7 zScDG5Us5bV7ZTr7EJF{H-&d?aA5vY#Dhwd~1H~E)A@j0g9Y&CSMX>>6$i1rAgbCz- zsMvz&HO&gphS=+hMd(1hr&xk6Bz~k=h8`r}P^>^7Qa@I#!T{25D%M~KnYR?{FoNvc ziVYY;?k9>(m_WX-*n;S%niZf8v7adxp#$-sE0&-OiC-v|p$ExdDpsHmsb48pVF2lO z6l*Yq%s{aYBgp<*u>oVqy{p)S3FLpH*n;S{niZf8vEL~cp#$;XE0&-OiT4!C(1YYq zu>yTa{Xww`14#c-u?9oP{7JD6Bgnq5*nlzQ{;b%93FQBx*n;SyTa{X?+|14w_USc4&C{;61p5oG_R*nlzQ#)?gtK>j1e7DOLw zR)99d7AY2?1MyE3OVEYHzZJ{SgXDh{E6|73|0z~s0O^Th4Tg~UuVNiW;=1gd@c$MY z`Z*3iXL7Sd6DE+Kt=NKSG0h6lhS(g%B6J|WxMB&qkcbq^(1YY$#R~KxHBYe$14u8S zSc4&CmQ<|62(q72Y`_?DODQ&C0{NvCTM)%GD?l4!%P1D11My`QOVEYHrxnZ4gXD6G z73f3iGm2FhKzez_8Vn(`f?^#;kc}%gU<|p>DmGyP`Ohh~Ao{#!1!zNTMa3d?ApQl# z5_BQ4l42QpkX%`@0)0p&6ss_R^eT!q7(!-M#X5{2`$feDj3Kw0ViP8iUtO^U(Hfc+ zpbfDvDHfpv@uXr2x{z2?u?#&(uBBLkKBVR=R$&0?wH0eHgv>gMbr?Z*UBw2BA-A4l z6DE*PDYhV5U$X+VA+~{H5jqh6vSJCkkobyX8G4Z1P_Y7iNNuE8g#n~DR;J9SCXnA$u?5j)niZf8vCS2W(1G|CiY4emVoSv`^dOm0tUw=9TPapy z0O_q2YcPb&Hi~r^L3Ufk28<#1HN_@OAiter3!?2cD?l4!S;ZoBAijfQ3A&KjQLzj? zNbaOqfj*?Zu2_Wuq<2=V!4NXLDAr*F*q~IR+zK!vBKhw+h*Uh>~(ubdrvPp#diAQ zQ&WrCaJKC$di$2OSvC%R6>n#Gn`84;y`60>9A`{DvNW;c>mr*z?rqxJTx*xmrl-#J zIM3o~zVJM2;Q^PheYlkUu#E+u!*Anl zf!g%cRLtVJ{v_Y_h09nw-`mCBmbEs^pX3s6pSJcFKZTciTh7`o-Y)a@8EZ{z;Z0rc zZFw6%Fdg2uZ8eW8*!UrDS9ptCYkRxW+h?sk>Fp|SpR@L~x2wH<-de}oHQvI<`@Fa5 zYdwC!;)@=y^R|+;uD80km972QS~w9i-V!!`%hz4+EnF|}d%M9}c;~CyerXukb`x!S z>WenH%;U{|@M_ks_15sVy0x3Fg$wT%Z)@22E?;*m4>(;dP z#BBTg!*-W1T+7BU_`;^Q`PRN17TI>Ux3z7&y~@J%_`I~lC+H~q(Z|mB);}`XP z*1}m^&-M@e`grhvw?xX~&f$`_t>p)=Z{vy`9M0lH-ZrrDx!xZ37GB(;{v?lh`--*0 zygh0yyr&Iq|JX0;$GlB%WO0$lwjaE)wOM}fLSLA+wyeFtaH~D(>o&1*!e8`L-o9$> zT6?^3S3PYld|aE_KF{8IxPzYab(`5Z_o*q)|MMOIdGow(%FjH;`>FFK*8t-oQ4tKO4TP zYIy0x*lx4i9a?IUk*d)vj@BEJ=X;%!%J zi`zSzUd0A|k2xD}7T%_9KlQeojkmG3s`)c(;q>or`wqVDm%eTfYkPS6mA5^u?eE9F z<83c%2l=|e;`aH6)4#XvANlA0YhSpJwME|E^_I8xiMQWa3y-s}?f>@nJ72e-HQk5C z@4fABc$l@Ty?yBIaBK6zX|wI0-j1;G zNw#jj`7dur+Blr<@MSgjR1;W7T&R9m?IHh8`LDO*tsQNT7rvOL>;eskoDkmsN*2R6(=3lA8}AvO z&9>RzPPFm${(y^lJIUG&*1{Lg9B<#W@v?sG;@(cSwz3}^c{|10!{M7Ood3BVPqpbW z{yEL_cAB;I{JVMyYvHlWwvX-h4BJw^?sOY};%#YfXIN|4IBYR*XWIB~dys9-WkL&| z|F>*E$q!!E7oKG;{JS2uPg@Ju$Jw?&V&kx_;Oow@@hm%b7c=gyV&l2~4fk1Z;d!?4 z_Bn6oTAM!6CSm)$$MbBwiH*aBv!WT^@%grg&pX_HEBm?&Y#jbY3tPfkc!3w%eu%f# zeBDLXj_|g+x2m-p?1AmBnx0z2VmRvyY|r?@q_=Nd`;&hpYx%(!TYKNyA?AE(6M;vCZR9<}`t|LDRQ3U6fkyY}Nyi{U~!(&J+`-rg4$ zy*+MiQ)}V#KgwI%#^Kv6e6B}(TWI4O?FEL9@EC7T*!Tfk7e2ycy*+8;2N$#ZKg8oa zK4s&V?BH;EkN5Vpjl=JFp`BnYoP}p>4}T-xXNE7Z(4MvZUVD;o*1qY-c5M89I32c~ z?Cm)lzZFi0ZKp)``G?c+yzM7?Jk=lY1#4&9e+;L$><7PS_(XZquP-^M#w3upH%Z(V!-sU0nb?HoV&2R07BG=_Ix@%FNfKk#?*ziZ|d zYiHSue%?IS*S#9X_5#C;KhN6_ZM>5`N7p>x+8pyW+ds6&TRS{()fc{Q<4gT4Eb#WT z@Gp{WAFn-ASuRR0ymh(gS1YU-{s0euT!%lV!{4>xFTxw`Q@DBdOmD4d?U{M=qYG#5 znIG*q^YQ%XpEL8;iPoLjVx8!Lg&(gI9X)HwwZa#&ZGS(E2fcE==;ck8T=j73W3Mkb v?8s>0VMoq=>ewZxdj5jmT$p@g?zVH5+~7^ZqE{E3{Px_1C%-*!!J_{MogsN| delta 15856 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 +## Step 1: Setting up environment +:display-partial{partial = "Foundry-zksync Installation"} ## Step 2: Set up wallet +Deploying contracts on the zkSync Sepolia testnet requires having testnet ETH. +If you're working within the local development environment, +you can utilize pre-configured rich wallets and skip this step. +For testnet deployments, follow these steps to secure your funds: + +:display-partial{partial = "Setting up your wallet"} + ## Step 3: Deploying your first contract -Now that we have setup our environment and our wallet, -we are ready to deploy our first contract! -For this tutorial we'll focus on the `/src/ZeekAdventures.sol` contract: +With our environment and wallet configured, we're set to deploy our first contract. This guide +introduces a crowdfunding campaign contract aimed at supporting Zeek's inventive ventures +Let's start by reviewing the starter contract in the `src/` directory. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -/** - * @title ZeekAdventures - * @dev Embark on adventures with Zeek, the playful cat mascot. This contract allows - * setting and retrieving Zeek's current adventure. - */ -contract ZeekAdventures { - // Zeek's current adventure - string private adventure; - - // Event to announce when Zeek's adventure is set - event AdventureSet(string newAdventure); - - /** - * @dev Constructor to set the initial adventure upon deployment. - * @param _initialAdventure The initial adventure Zeek will embark on. - */ - constructor(string memory _initialAdventure) { - adventure = _initialAdventure; - emit AdventureSet(_initialAdventure); +contract CrowdfundingCampaign { + address public owner; + uint256 public fundingGoal; + uint256 public totalFundsRaised; + mapping(address => uint256) public contributions; + + event ContributionReceived(address contributor, uint256 amount); + event GoalReached(uint256 totalFundsRaised); + + constructor(uint256 _fundingGoal) { + owner = msg.sender; + fundingGoal = _fundingGoal; } - /** - * @dev Sets a new adventure for Zeek. - * @param _newAdventure The new adventure Zeek will embark on. - */ - function setAdventure(string memory _newAdventure) public { - adventure = _newAdventure; - emit AdventureSet(_newAdventure); + function contribute() public payable { + require(msg.value > 0, "Contribution must be greater than 0"); + contributions[msg.sender] += msg.value; + totalFundsRaised += msg.value; + + emit ContributionReceived(msg.sender, msg.value); + + if (totalFundsRaised >= fundingGoal) { + emit GoalReached(totalFundsRaised); + } } - /** - * @dev Returns the current adventure Zeek is on. - * @return The current adventure of Zeek. - */ - function currentAdventure() public view returns (string memory) { - return adventure; + function withdrawFunds() public { + require(msg.sender == owner, "Only the owner can withdraw funds"); + require(totalFundsRaised >= fundingGoal, "Funding goal not reached"); + + uint256 amount = address(this).balance; + totalFundsRaised = 0; + + (bool success, ) = payable(owner).call{value: amount}(""); + require(success, "Transfer failed."); + } + + function getTotalFundsRaised() public view returns (uint256) { + return totalFundsRaised; + } + + function getFundingGoal() public view returns (uint256) { + return fundingGoal; } } ``` +The `CrowdfundingCampaign` contract is designed for project crowdfunding. +Owned and deployed with a set funding goal, it features: + +- A constructor to initialize the campaign's funding target. +- The `contribute` method to log funds, triggering `ContributionReceived` and `GoalReached` events. +- The `withdrawFunds` method, allowing the owner to collect accumulated funds post-goal achievement. + ### Compile contract +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + To compile the contracts in the project, run the following command: ```bash forge build --zksync ``` -**You'll get the following output:** +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. ```bash -TODO +[⠒] Compiling... +[⠃] Compiling 21 files with 0.8.20 +[⠊] Solc 0.8.20 finished in 736.48ms +Compiler run successful! +Compiling contracts for zkSync Era with zksolc v1.4.0 ``` -The compiled artifacts will be located in the `/zkout` folder. +The compiled zkEVM artifacts will be located in the `/zkout` folder, and the solc artifacts will be +located in the `/out` folder. ### Deploy -In this section, we’ll get to deploy the ZeekAdventure contract -onto {network}. -The script to deploy contracts is found at `/scripts/deploy.s.sol`. +This section outlines the steps to deploy the `CrowdfundingCampaign` contract. +The deployment script is located at `/script/Deploy.s.sol`. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; -// TODO: -@Dustin -Write foundry script for deployment. Add and explain here. +import "forge-std/Script.sol"; +import "../src/Crowdfund.sol"; -To deploy the contract, run the following command: +contract DeployCrowdfundContract is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("WALLET_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + uint256 fundingGoalInWei = 0.02 ether; + new CrowdfundingCampaign(fundingGoalInWei); + + vm.stopBroadcast(); + } +} +``` + +Key Components: + +- **contractArtifactName:** Identifies the `CrowdfundingCampaign` contract for deployment. +- **constructorArguments:** Sets initialization parameters for the contract. + +Execute the deployment command. ```bash -forge script command --zksync +forge script script/Deploy.s.sol:DeployCrowdfundContract --rpc-url zkSyncSepoliaTestnet --broadcast --zksync +# To deploy the contract on local in-memory node: +# forge script script/Deploy.s.sol:DeployCrowdfundContract --rpc-url inMemoryNode --broadcast --zksync ``` -**You'll get the following output:** +#### Expected Output + +Upon successful deployment, you'll receive output detailing the deployment process, +including the contract address, transaction hash, and block number deployed to: ```bash -TODO +Script ran successfully. + +## Setting up 1 EVM. + +========================== + +Chain 300 + +Estimated gas price: .1 gwei + +Estimated total gas used for script: 481392 + +Estimated amount required: 0.00015404544 ETH + +========================== + +### +Finding wallets for all the necessary addresses... +## +Sending transactions [0 - 0]. +⠁ [00:00:00] [###########################################################################################################################################################################################################] 1/1 txes (0.0s) + +## +Waiting for receipts. +⠉ [00:00:06] [#######################################################################################################################################################################################################] 1/1 receipts (0.0s) +##### 300 +✅ [Success]Hash: 0x69f5f1f0f5b3fa12ed2fbab4d6bb6edc02bbfff2f8c414d8171cc8295250296c +Contract Address: 0xB0C0d3d02c270b6ABe4862EA90bBa1Af192314a8 +Block: 1491370 +Paid: 0.0001168854 ETH (1168854 gas * 0.1 gwei) ``` -🥳 Congratulations! You just deployed a smart contract to {network}! +🥳 Congratulations! Your smart contract is now deployed. 🚀 diff --git a/content/10.quick-code/_index/_hardhat_deploy_contract.md b/content/10.quick-code/_index/_hardhat_deploy_contract.md index 11a2ed0d..de126ffb 100644 --- a/content/10.quick-code/_index/_hardhat_deploy_contract.md +++ b/content/10.quick-code/_index/_hardhat_deploy_contract.md @@ -18,58 +18,72 @@ For testnet deployments, follow these steps to secure your funds: :display-partial{partial = "Setting up your wallet"} ## Step 3: Deploying your first contract -:display-partial{partial = "Deploy Contract"} -Now that we have setup our environment and our wallet, we are ready to deploy our first contract! -For this tutorial we'll focus on the `/contracts/ZeekAdventures.sol` contract: +With our environment and wallet configured, we're set to deploy our first contract. This guide +introduces a crowdfunding campaign contract aimed at supporting Zeek's inventive ventures +Let's start by reviewing the starter contract in the `contracts/` directory. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -/** - * @title ZeekAdventures - * @dev Embark on adventures with Zeek, the playful cat mascot. This contract allows - * setting and retrieving Zeek's current adventure. - */ -contract ZeekAdventures { - // Zeek's current adventure - string private adventure; - - // Event to announce when Zeek's adventure is set - event AdventureSet(string newAdventure); - - /** - * @dev Constructor to set the initial adventure upon deployment. - * @param _initialAdventure The initial adventure Zeek will embark on. - */ - constructor(string memory _initialAdventure) { - adventure = _initialAdventure; - emit AdventureSet(_initialAdventure); +contract CrowdfundingCampaign { + address public owner; + uint256 public fundingGoal; + uint256 public totalFundsRaised; + mapping(address => uint256) public contributions; + + event ContributionReceived(address contributor, uint256 amount); + event GoalReached(uint256 totalFundsRaised); + + constructor(uint256 _fundingGoal) { + owner = msg.sender; + fundingGoal = _fundingGoal; + } + + function contribute() public payable { + require(msg.value > 0, "Contribution must be greater than 0"); + contributions[msg.sender] += msg.value; + totalFundsRaised += msg.value; + + emit ContributionReceived(msg.sender, msg.value); + + if (totalFundsRaised >= fundingGoal) { + emit GoalReached(totalFundsRaised); + } + } + + function withdrawFunds() public { + require(msg.sender == owner, "Only the owner can withdraw funds"); + require(totalFundsRaised >= fundingGoal, "Funding goal not reached"); + + uint256 amount = address(this).balance; + totalFundsRaised = 0; + + (bool success, ) = payable(owner).call{value: amount}(""); + require(success, "Transfer failed."); } - /** - * @dev Sets a new adventure for Zeek. - * @param _newAdventure The new adventure Zeek will embark on. - */ - function setAdventure(string memory _newAdventure) public { - adventure = _newAdventure; - emit AdventureSet(_newAdventure); + function getTotalFundsRaised() public view returns (uint256) { + return totalFundsRaised; } - /** - * @dev Returns the current adventure Zeek is on. - * @return The current adventure of Zeek. - */ - function currentAdventure() public view returns (string memory) { - return adventure; + function getFundingGoal() public view returns (uint256) { + return fundingGoal; } } ``` +The `CrowdfundingCampaign` contract is designed for project crowdfunding. +Owned and deployed with a set funding goal, it features: + +- A constructor to initialize the campaign's funding target. +- The `contribute` method to log funds, triggering `ContributionReceived` and `GoalReached` events. +- The `withdrawFunds` method, allowing the owner to collect accumulated funds post-goal achievement. + ### Compile contract -Smart contracts deployed to zkSync must be compiled using our custom compilers. +Smart contracts deployed to zkSync must be compiled using our custom compiler. For this particular guide we are making use of `zksolc`. To compile the contracts in the project, run the following command: @@ -77,75 +91,111 @@ To compile the contracts in the project, run the following command: ::code-group ```bash [yarn] -yarn compile:contracts +yarn compile ``` ```bash [pnpm] -pnpm run compile:contracts +pnpm run compile ``` ```bash [npm] -npm run compile:contracts +npm run compile ``` ```bash [bun] -bun run compile:contracts +bun run compile ``` :: -**You'll get the following output:** +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. ```bash -Compiling contracts for zkSync Era with zksolc v1.4.1 and solc v0.8.23 -Compiling 1 Solidity files -Successfully compiled 1 Solidity files -✨ Done in 1.55s. +Compiling contracts for zkSync Era with zksolc v1.4.0 and solc v0.8.17 +Compiling 1 Solidity file +Successfully compiled 1 Solidity file ``` The compiled artifacts will be located in the `/artifacts-zk` folder. ### Deploy -In this section, we’ll get to deploy the ZeekAdventure contract -onto {network}. -The script to deploy contracts is found at `/deploy/deploy.ts`. +This section outlines the steps to deploy the `CrowdfundingCampaign` contract. +The deployment script is located at `/deploy/deploy.ts`. + +```typescript +import { deployContract } from "./utils"; +import { ethers } from "ethers"; + +// An example of a basic deploy script +// It will deploy a CrowdfundingCampaign contract to selected network +// `parseEther` converts ether to wei, and `.toString()` ensures serialization compatibility. +export default async function () { + const contractArtifactName = "CrowdfundingCampaign"; + const constructorArguments = [ethers.parseEther('.02').toString()]; + await deployContract(contractArtifactName, constructorArguments); +} +``` + +Key Components: -The `contractArtifactName` defines the contract we want to deploy.Here we have it set to our contract, ZeekAdventure. -Similarly, `constructorArguments` are the arguments we need to provide to the constructor to initialize the contract. +- **contractArtifactName:** Identifies the `CrowdfundingCampaign` contract for deployment. +- **constructorArguments:** Sets initialization parameters for the contract. In this case, +the fundraising goal, converted from ether to `wei` to match Solidity's `uint256` type. -To deploy the contract, run the following command: +Execute the deployment command corresponding to your package manager. The default command +deploys to the configured network in your Hardhat setup. For local deployment, append +`--network inMemoryNode` to deploy to the local in-memory node running. ::code-group ```bash [yarn] -yarn deploy +yarn hardhat deploy-zksync --script deploy.ts +# To deploy the contract on local in-memory node: +# yarn hardhat deploy-zksync --script deploy.ts --network inMemoryNode ``` ```bash [pnpm] -pnpm run deploy +pnpm run hardhat deploy-zksync --script deploy.ts +# To deploy the contract on local in-memory node: +# pnpm run hardhat deploy-zksync --script deploy.ts --network inMemoryNode ``` ```bash [npm] -npm run deploy +npm run hardhat deploy-zksync --script deploy.ts +# To deploy the contract on local in-memory node: +# npm run hardhat deploy-zksync --script deploy.ts --network inMemoryNode ``` ```bash [bun] -bun run deploy +bun run hardhat deploy-zksync --script deploy.ts +# To deploy the contract on local in-memory node: +# bun run hardhat deploy-zksync --script deploy.ts --network inMemoryNode ``` :: -**You'll get the following output:** +#### Expected Output + +Upon successful deployment, you'll receive output detailing the deployment process, +including the contract address, source, and encoded constructor arguments: ```bash -Starting deployment process of "ZeekAdventures"... -Estimated deployment cost: 0.0000648863 ETH +Starting deployment process of "CrowdfundingCampaign"... +Estimated deployment cost: 0.000501 ETH + +"CrowdfundingCampaign" was successfully deployed: + - Contract address: 0x4E3404F21b29d069539e15f8f9E712CeAE39d90C + - Contract source: contracts/Crowdfund.sol:CrowdfundingCampaign + - Encoded constructor arguments: 0x00000000000000000000000000000000000000000000000000470de4df820000 -"ZeekAdventures" was successfully deployed: - - Contract address: 0x0BaF96A7f137B05d0D35b76d59B16c86C1791D8D - - Contract source: contracts/ZeekAdventures.sol:ZeekAdventures - - Encoded constructor arguments: 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000 +Requesting contract verification... +Your verification ID is: 10067 +Contract successfully verified on zkSync block explorer! ``` -🥳 Congratulations! You just deployed a smart contract to {network}! +🥳 Congratulations! Your smart contract is now deployed. 🚀 diff --git a/content/_partials/environment-setup-with-foundry.md b/content/_partials/environment-setup-with-foundry.md new file mode 100644 index 00000000..63206ff5 --- /dev/null +++ b/content/_partials/environment-setup-with-foundry.md @@ -0,0 +1,43 @@ +--- +title: Foundry-zksync Installation +--- + +::drop-panel + ::panel{label="Install foundry-zksync"} + Begin by installing `foundry-zksync` in your environment using the command below: + + ``` + curl -L https://foundry-zksync.zksync.io | bash + ``` + :: + + ::panel{label="Setup local node (optional)"} + Leveraging **zksync-cli** to set up + a local development environment is a beneficial next step. + This local setup allows for quicker testing and debugging + processes without incurring testnet transaction costs. + + **Prerequisites:** + + **Docker Installation:** **zksync-cli**'s local environment relies on Docker. + Download the appropriate version from the Docker website. + If you're new to Docker, consider exploring the Docker getting started guide. + + 1. **Open Your Terminal:** Access it from the Applications folder or use Spotlight search (Command + Space). + 2. **Initialize Local zkSync Node:** + - Run the command: **npx zksync-cli@latest dev start** + - When prompted, choose “In memory node” to deploy a local zkSync node in a Docker container. + 3. **Access Rich Accounts:** For pre-configured rich wallets, visit [era-test-node rich wallets](https://era.zksync.io/docs/tools/testing/era-test-node.html#use-pre-configured-rich-wallets). + 4. **Local Node URL:** Your local zkSync node is accessible at + **[http://127.0.0.1:8011](http://127.0.0.1:8011/)**, ready for deployment or testing purposes. + :: + + ::panel{label="Initialize project"} + Run the following command in your terminal to create a new Foundry project using `foundry-zksync`. + + ```sh + forge init --template https://github.com/dutterbutter/zksync-foundry-quickstart-guide + ``` + + :: +:: diff --git a/content/_partials/setting-up-your-wallet.md b/content/_partials/setting-up-your-wallet.md index 7130f6c8..4f7e6a89 100644 --- a/content/_partials/setting-up-your-wallet.md +++ b/content/_partials/setting-up-your-wallet.md @@ -32,7 +32,7 @@ title: Setting up your wallet - Open the `.env` file and add your private key in the following format: ```sh - PRIVATE_KEY=your_private_key_here + WALLET_PRIVATE_KEY=your_private_key_here ``` - Replace **`your_private_key_here`** with your actual private key. From 308e6ae60a151127496dbebdf929b2b9ad01aace Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Fri, 5 Apr 2024 15:23:30 -0500 Subject: [PATCH 07/54] chore: Foundry content, and minor improvements --- content/10.quick-code/1.index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/10.quick-code/1.index.md b/content/10.quick-code/1.index.md index de4b2dd4..8b2235e9 100644 --- a/content/10.quick-code/1.index.md +++ b/content/10.quick-code/1.index.md @@ -21,7 +21,7 @@ Select the framework you want to get started using zkSync Era with. ::content-switcher --- items: [{ - label: 'HardHat', + label: 'Hardhat', partial: '_index/_hardhat_deploy_contract' }, { label: 'Foundry', @@ -34,7 +34,6 @@ items: [{ - **EVM Compatibility:** zkSync is EVM compatible and you can write smart contracts in Solidity or Vyper. - **Development Tools:** zkSync supports your favorite development toolkit Hardhat and Foundry. -- **zkSync-cli:** Provides a quick way to scaffold different types of projects thanks to its multiple templates. - **Custom Compilation:** Contracts deployed to zkSync are compiled using `zksolc` or `zkvyper` as they generate a special bytecode for zkSync's ZKEVM. From f4711619a5904fb596be92d42732a6e676e8f87f Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Fri, 5 Apr 2024 15:27:25 -0500 Subject: [PATCH 08/54] chore: rename dir to quick-start instead of quick-code --- content/10.quick-code/_dir.yml | 2 -- content/{10.quick-code => 10.quick-start}/1.index.md | 0 content/{10.quick-code => 10.quick-start}/2.deploy-factory.md | 0 content/{10.quick-code => 10.quick-start}/3.testing.md | 0 content/{10.quick-code => 10.quick-start}/4.upgrading.md | 0 content/{10.quick-code => 10.quick-start}/5.verifying.md | 0 content/{10.quick-code => 10.quick-start}/6.paymaster.md | 0 content/{10.quick-code => 10.quick-start}/7.smart-account.md | 0 content/10.quick-start/_dir.yml | 2 ++ .../_index/_foundry_deploy_contract.md | 0 .../_index/_hardhat_deploy_contract.md | 0 content/index.yml | 2 +- 12 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 content/10.quick-code/_dir.yml rename content/{10.quick-code => 10.quick-start}/1.index.md (100%) rename content/{10.quick-code => 10.quick-start}/2.deploy-factory.md (100%) rename content/{10.quick-code => 10.quick-start}/3.testing.md (100%) rename content/{10.quick-code => 10.quick-start}/4.upgrading.md (100%) rename content/{10.quick-code => 10.quick-start}/5.verifying.md (100%) rename content/{10.quick-code => 10.quick-start}/6.paymaster.md (100%) rename content/{10.quick-code => 10.quick-start}/7.smart-account.md (100%) create mode 100644 content/10.quick-start/_dir.yml rename content/{10.quick-code => 10.quick-start}/_index/_foundry_deploy_contract.md (100%) rename content/{10.quick-code => 10.quick-start}/_index/_hardhat_deploy_contract.md (100%) diff --git a/content/10.quick-code/_dir.yml b/content/10.quick-code/_dir.yml deleted file mode 100644 index 46117271..00000000 --- a/content/10.quick-code/_dir.yml +++ /dev/null @@ -1,2 +0,0 @@ -title: QuickCode -layout: docs diff --git a/content/10.quick-code/1.index.md b/content/10.quick-start/1.index.md similarity index 100% rename from content/10.quick-code/1.index.md rename to content/10.quick-start/1.index.md diff --git a/content/10.quick-code/2.deploy-factory.md b/content/10.quick-start/2.deploy-factory.md similarity index 100% rename from content/10.quick-code/2.deploy-factory.md rename to content/10.quick-start/2.deploy-factory.md diff --git a/content/10.quick-code/3.testing.md b/content/10.quick-start/3.testing.md similarity index 100% rename from content/10.quick-code/3.testing.md rename to content/10.quick-start/3.testing.md diff --git a/content/10.quick-code/4.upgrading.md b/content/10.quick-start/4.upgrading.md similarity index 100% rename from content/10.quick-code/4.upgrading.md rename to content/10.quick-start/4.upgrading.md diff --git a/content/10.quick-code/5.verifying.md b/content/10.quick-start/5.verifying.md similarity index 100% rename from content/10.quick-code/5.verifying.md rename to content/10.quick-start/5.verifying.md diff --git a/content/10.quick-code/6.paymaster.md b/content/10.quick-start/6.paymaster.md similarity index 100% rename from content/10.quick-code/6.paymaster.md rename to content/10.quick-start/6.paymaster.md diff --git a/content/10.quick-code/7.smart-account.md b/content/10.quick-start/7.smart-account.md similarity index 100% rename from content/10.quick-code/7.smart-account.md rename to content/10.quick-start/7.smart-account.md diff --git a/content/10.quick-start/_dir.yml b/content/10.quick-start/_dir.yml new file mode 100644 index 00000000..f29ef524 --- /dev/null +++ b/content/10.quick-start/_dir.yml @@ -0,0 +1,2 @@ +title: Quickstart +layout: docs diff --git a/content/10.quick-code/_index/_foundry_deploy_contract.md b/content/10.quick-start/_index/_foundry_deploy_contract.md similarity index 100% rename from content/10.quick-code/_index/_foundry_deploy_contract.md rename to content/10.quick-start/_index/_foundry_deploy_contract.md diff --git a/content/10.quick-code/_index/_hardhat_deploy_contract.md b/content/10.quick-start/_index/_hardhat_deploy_contract.md similarity index 100% rename from content/10.quick-code/_index/_hardhat_deploy_contract.md rename to content/10.quick-start/_index/_hardhat_deploy_contract.md diff --git a/content/index.yml b/content/index.yml index 874ef19b..0769ee37 100644 --- a/content/index.yml +++ b/content/index.yml @@ -15,7 +15,7 @@ hero: - label: Start building on zkSync icon: i-heroicons-arrow-right-20-solid trailing: true - to: '/quick-code' + to: '/quick-start' size: lg # - label: Use this template # icon: i-simple-icons-github From 03aa7d859d79a5816615a04b74885e92b8f0cd18 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Sat, 6 Apr 2024 21:48:40 -0500 Subject: [PATCH 09/54] feat: adds deploy factories and testing, still some issues to resolve with components --- content/10.quick-start/2.deploy-factory.md | 75 +++-- content/10.quick-start/3.testing.md | 178 +++--------- .../_foundry_deploy_contract_factory.md | 183 ++++++++++++ .../_hardhat_deploy_contract_factory.md | 221 +++++++++++++++ .../_index/_foundry_deploy_contract.md | 31 +-- .../_index/_hardhat_deploy_contract.md | 1 + .../_testing/_foundry_contract_testing.md | 216 ++++++++++++++ .../_testing/_hardhat_contract_testing.md | 263 ++++++++++++++++++ 8 files changed, 983 insertions(+), 185 deletions(-) create mode 100644 content/10.quick-start/_deploy_factory/_foundry_deploy_contract_factory.md create mode 100644 content/10.quick-start/_deploy_factory/_hardhat_deploy_contract_factory.md create mode 100644 content/10.quick-start/_testing/_foundry_contract_testing.md create mode 100644 content/10.quick-start/_testing/_hardhat_contract_testing.md diff --git a/content/10.quick-start/2.deploy-factory.md b/content/10.quick-start/2.deploy-factory.md index 995b412a..a9721260 100644 --- a/content/10.quick-start/2.deploy-factory.md +++ b/content/10.quick-start/2.deploy-factory.md @@ -1,32 +1,69 @@ --- title: Contract Factories -description: Get started with Nuxt UI Pro documentation template. +description: Learn to deploy contract factories in the zkSync environment. --- -::callout{icon="i-heroicons-light-bulb"} -This is a callout. +Welcome back to the Quickstart Series on becoming proficient with zkSync development! +This second installment advances from your introductory exploration of smart contract +deployment to dive into the utility of contract factories. Through this guide, +you'll learn how to streamline the deployment of multiple crowdfunding campaigns +using a single contract factory, leveraging the foundational `CrowdfundingCampaign` +contract in the first guide. + +:check-icon Advancing your zkSync development journey with contract factories. + +:check-icon Constructing a contract factory to create multiple crowdfunding campaigns for Zeek. + +:check-icon Seamlessly deploying your contract factory on zkSync Era, using either Hardhat or Foundry. + +Let's explore the efficiency and scalability that contract factories bring. + +::callout +**What is a contract factory?**
+A contract factory is a design pattern that allows for the creation of multiple +contract instances from a single "factory" contract. It's essentially a contract +that creates other contracts, streamlining and organizing the deployment of +numerous similar contracts efficiently. :: ---- +## Framework selection -Use this template on Nuxt Studio and start your documentation in seconds. :: +Select the framework you want to get started using zkSync Era with. -## Quick Start +::content-switcher +--- +items: [{ + label: 'Hardhat', + partial: '_deploy_factory/_hardhat_deploy_contract_factory' +}, { + label: 'Foundry', + partial: '_deploy_factory/_foundry_deploy_contract_factory' +}] +--- +:: -You can start a fresh new project with: +## Takeaways -```bash [Terminal] -npx nuxi init -t github:nuxt-ui-pro/docs -``` +- **Contract Factories:** Utilizing contract factories significantly streamlines +the deployment process, allowing for the creation of multiple instances of a +contract, like the `CrowdfundingCampaign`, with varied parameters. +- **Scalability and Management:** Contract factories offer a scalable solution to manage +numerous contract instances, enhancing project organization and efficiency. +- **Event-Driven Insights:** The `CampaignCreated` event in the factory contract provides +a transparent mechanism to track each crowdfunding campaign's deployment, useful for +off-chain monitoring and interaction. -or create a new repository from GitHub: +## Next steps -1. Open -2. Click on `Use this template` button -3. Enter repository name and click on `Create repository from template` button -4. Clone your new repository -5. Install dependencies with your favorite package manager -6. Start development server +With the contract factory in your zkSync development arsenal, you're set to elevate +your smart contract projects. Here's how you can further your journey: -That's it! You can now start writing your documentation in the -[`content/`](https://content.nuxt.com/usage/content-directory) directory 🚀 +- **Contract Testing:** Progress to the next guide focused on testing your contracts. +Ensuring the reliability and security of your `CrowdfundingCampaign` through +comprehensive tests is critical. +- **Advanced zkSync Integrations:** Explore deeper into zkSync's ecosystem by +implementing features like account abstraction and paymasters to enhance user +experience and contract flexibility. +- **Community Engagement and Contribution:** Join the vibrant zkSync community. +Participate in forums, Discord, or GitHub discussions. Sharing insights, asking queries, +and contributing can enrich the ecosystem and your understanding of zkSync. diff --git a/content/10.quick-start/3.testing.md b/content/10.quick-start/3.testing.md index 0c940a7d..72caa9f8 100644 --- a/content/10.quick-start/3.testing.md +++ b/content/10.quick-start/3.testing.md @@ -1,155 +1,55 @@ --- title: Testing -description: Learn how to write and customize your documentation. +description: Learn to test smart contracts efficiently in the zkSync environment. --- -This is only a basic example of what you can achieve with [Nuxt UI Pro](https://ui.nuxt.com/pro/guide), you can tweak it -to match your needs. The template uses several Nuxt modules underneath like [`@nuxt/content`](https://content.nuxt.com) -for the content, [`@nuxtjs/fontaine`](https://github.com/nuxt-modules/fontaine) and -[`@nuxtjs/google-fonts`](https://github.com/nuxt-modules/google-fonts) to change the font and -[`nuxt-og-image`](https://nuxtseo.com/og-image/getting-started/installation) for social previews. +Welcome back to our Quickstart Series, your fast-track to zkSync development! In this +third guide, we transition from deploying and managing contracts to the critical phase +of testing. This guide will walk you through the steps to ensure your `CrowdfundingCampaign` +contracts, introduced in our first guide and efficiently deployed through contract factories +in the second, work flawlessly. -## ::callout +:check-icon Elevating your zkSync toolkit with robust contract testing techniques. -icon: i-heroicons-light-bulb target: \_blank to: [nuxt guide](https://ui.nuxt.com/pro/guide/usage#structure) +:check-icon Crafting comprehensive tests for your `CrowdfundingCampaign` to ensure reliability and security. ---- - -Learn more on how to customize and structure a Nuxt UI Pro app! :: - -## Writing content - -You can just start writing `.md` or `.yml` files in the [`content/`](https://content.nuxt.com/usage/content-directory) -directory to have your pages updated. The navigation will be automatically generated in the left aside and in the mobile -menu. You will also be able to go through your content with full-text search. - -::callout{icon="i-heroicons-light-bulb"} This template relies on a -[catch-all route](https://nuxt.com/docs/guide/directory-structure/pages#catch-all-route) but you can achieve the same -thing with the [document-driven mode](https://content.nuxt.com/document-driven/introduction). :: +:check-icon Using Hardhat or Foundry to write and run tests, ensuring your campaigns are ready. -## App Configuration +Dive into the world of smart contract testing and solidify the foundation of your zkSync projects. -In addition to `@nuxt/ui-pro` configuration through the `app.config.ts`, this template lets you customize the `Header`, -`Footer` and the `Table of contents` components. +## Framework selection -### Header +Select the framework you want to get started using zkSync Era with. -```ts [app.config.ts] -export default defineAppConfig({ - header: { - // Logo configuration - logo: { - // Light mode - light: { - src: '', - alt: '', - class: '', - }, - // Dark mode - dark: { - src: '', - alt: '', - class: '', - }, - }, - // Show or hide the search bar - search: true, - // Show or hide the color mode button - colorMode: true, - // Customize links - links: [ - { - icon: 'i-simple-icons-github', - to: 'https://github.com/nuxt-ui-pro/docs', - target: '_blank', - 'aria-label': 'Docs template on GitHub', - }, - ], - }, -}); -``` - -### Footer - -```ts [app.config.ts] -export default defineAppConfig({ - footer: { - // Update bottom left credits - credits: 'Copyright © 2023', - // Show or hide the color mode button - colorMode: false, - // Customize links - links: [ - { - icon: 'i-simple-icons-nuxtdotjs', - to: 'https://nuxt.com', - target: '_blank', - 'aria-label': 'Nuxt Website', - }, - { - icon: 'i-simple-icons-discord', - to: 'https://discord.com/invite/ps2h6QT', - target: '_blank', - 'aria-label': 'Nuxt UI on Discord', - }, - { - icon: 'i-simple-icons-x', - to: 'https://x.com/nuxt_js', - target: '_blank', - 'aria-label': 'Nuxt on X', - }, - { - icon: 'i-simple-icons-github', - to: 'https://github.com/nuxt/ui', - target: '_blank', - 'aria-label': 'Nuxt UI on GitHub', - }, - ], - }, -}); -``` +::content-switcher +--- +items: [{ + label: 'Hardhat', + partial: '_testing/_hardhat_contract_testing' +}, { + label: 'Foundry', + partial: '_testing/_foundry_contract_testing' +}] +--- +:: -### Table of contents +## Takeaways -```ts [app.config.ts] -export default defineAppConfig({ - toc: { - // Title of the main table of contents - title: 'Table of Contents', - // Bottom TOC configuration - bottom: { - // Title of the bottom table of contents - title: 'Community', - // URL of your repository content folder - edit: '', - // Customize links - links: [ - { - icon: 'i-heroicons-star', - label: 'Star on GitHub', - to: 'https://github.com/nuxt/ui', - target: '_blank', - }, - { - icon: 'i-heroicons-book-open', - label: 'Nuxt UI Pro docs', - to: 'https://ui.nuxt.com/pro/guide', - target: '_blank', - }, - { - icon: 'i-simple-icons-nuxtdotjs', - label: 'Purchase a license', - to: 'https://ui.nuxt.com/pro/purchase', - target: '_blank', - }, - ], - }, - }, -}); -``` +- **EVM Compatibility:** zkSync is EVM compatible and you can write smart contracts in Solidity or Vyper. +- **Development Tools:** zkSync supports your favorite development toolkit Hardhat and Foundry. +- **Custom Compilation:** Contracts deployed to zkSync are compiled using `zksolc` or `zkvyper` as +they generate a special bytecode for zkSync's ZKEVM. -icon: i-heroicons-light-bulb target: \_blank to: [app-config](https://nuxt.studio/docs/developers/app-config) +## Next steps ---- +Having successfully deployed your first contract on zkSync, you're well on your way to becoming +a proficient zkSync developer. To expand your expertise: -A dedicated interface is provided to edit those configurations on Nuxt Studio. :: +- **Explore Contract Factories:** Enhance your project by building a contract factory +for the `CrowdfundingCampaign` contract in the next guide. This will allow you to efficiently +manage multiple crowdfunding campaigns, each with its own unique parameters. +- **Dive Deeper into zkSync Features:** Investigate advanced zkSync features such as account abstraction, +and paymasters. +- **Join the Community:** Engage with the zkSync developer community through forums, +Discord channels, Dev Discussions, or GitHub repositories. Share your experiences, ask questions, +and collaborate on projects. diff --git a/content/10.quick-start/_deploy_factory/_foundry_deploy_contract_factory.md b/content/10.quick-start/_deploy_factory/_foundry_deploy_contract_factory.md new file mode 100644 index 00000000..60ab8ab5 --- /dev/null +++ b/content/10.quick-start/_deploy_factory/_foundry_deploy_contract_factory.md @@ -0,0 +1,183 @@ +--- +title: Foundry | Deploy Contract Factory +--- + +`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. +It extends Foundry's capabilities for Ethereum app development to support zkSync, +allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. + +::callout{icon="i-heroicons-information-circle-16-solid" color="amber"} +`foundry-zksync` is still in an alpha stage, so some features might not be fully supported +yet and may not work as fully intended. It is open-sourced and contributions are welcomed. +:: + + +## Step 1: Setting up environment +:display-partial{partial = "Foundry-zksync Installation"} + +## Step 2: Set up wallet + +Deploying contracts on the zkSync Sepolia testnet requires having testnet ETH. +If you're working within the local development environment, +you can utilize pre-configured rich wallets and skip this step. +For testnet deployments, follow these steps to secure your funds: + +:display-partial{partial = "Setting up your wallet"} + +## Step 3: Deploying your first contract + +With our environment and wallet configured, we're set to review the `CrowdfundingFactory.sol` +contract that is provided during the initialization step in the `/src` directory. +The `CrowdfundingFactory.sol`contract will be used to deploy multiple instances of +the `CrowdfundingCampaign.sol` contract from the previous guide. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Crowdfunding campaign contract +import "./CrowdfundingCampaign.sol"; + +// Factory contract to create and manage crowdfunding campaigns +contract CrowdfundingFactory { + CrowdfundingCampaign[] public campaigns; + + event CampaignCreated(address campaignAddress, uint256 fundingGoal); + + function createCampaign(uint256 fundingGoal) public { + CrowdfundingCampaign newCampaign = new CrowdfundingCampaign(fundingGoal); + campaigns.push(newCampaign); + + emit CampaignCreated(address(newCampaign), fundingGoal); + } + + function getCampaigns() public view returns (CrowdfundingCampaign[] memory) { + return campaigns; + } +} +``` + +The `CrowdfundingFactory` contract automates the creation and oversight of +`CrowdfundingCampaign` contracts, each with its distinct funding goals, it features: + +- **Campaign Creation**: Utilizes the `createCampaign` method to initiate a new +`CrowdfundingCampaign` contract. This function takes a `fundingGoal` as an argument, +deploys a new campaign contract with this goal, and tracks the created campaign in the +`campaigns` array. +- **Campaign Tracking**: The `getCampaigns` method offers a view into all the campaigns +created by the factory, allowing for easy access and management of multiple crowdfunding +initiatives. + +This contract factory approach streamlines the deployment of crowdfunding campaigns, +making it efficient to launch and manage multiple campaigns concurrently. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +```bash +forge build --zksync +``` + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +[⠒] Compiling... +[⠃] Compiling 22 files with 0.8.20 +[⠊] Solc 0.8.20 finished in 736.48ms +Compiler run successful! +Compiling contracts for zkSync Era with zksolc v1.4.0 +``` + +The compiled zkEVM artifacts will be located in the `/zkout` folder, and the solc +artifacts will be located in the `/out` folder. + +### Deploy + +This section outlines the steps to deploy the `CrowdfundingCampaign` contract using +our new `CrowdfundingFactory`. + +The deployment script is located at `/script/deployUsingFactory.s.sol`. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "forge-std/console.sol"; +import "../src/CrowdfundFactory.sol"; +import "../src/CrowdfundingCampaign.sol"; + +contract DeployFactoryAndCreateCampaign is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("WALLET_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + // Deploy the CrowdfundingFactory contract + CrowdfundingFactory factory = new CrowdfundingFactory(); + + // Log the factory's address + console.log("CrowdfundingFactory deployed at: %s", address(factory)); + + // Define the funding goal for the new campaign + uint256 fundingGoalInWei = 0.01 ether; + + // Use the factory to create a new CrowdfundingCampaign + factory.createCampaign(fundingGoalInWei); + + // Not sure how to get the address of the new campaign + // TODO: Log the address of the new campaign + + vm.stopBroadcast(); + } +} +``` + +Key Components: + +**Deployment Workflow:** + +- Initiates by deploying the `CrowdfundingFactory` through `deployContract`. +- Using the factory's deployed address to form a `factoryContract` instance, +facilitating access to the factory's functionalities. + +**Initiating Campaigns:** + +- Executes the `factoryContract`'s `createCampaign` function to create and deploy a new +crowdfunding campaign, with the specified funding target. + +#### Deploy factory +Execute the deployment command. + +```bash +forge script script/DeployFactory.s.sol:DeployFactoryAndCreateCampaign --rpc-url zkSyncSepoliaTestnet --broadcast --zksync +# To deploy the contract on local in-memory node: +# forge script script/DeployFactory.s.sol:DeployFactoryAndCreateCampaign --rpc-url inMemoryNode --broadcast --zksync +``` + +#### Expected Output + +Upon successful deployment, you'll receive output detailing the deployment process, +including the contract address, transaction hash, and block number deployed to: + +```bash +== Logs == + CrowdfundingFactory deployed at: 0xdE663Eccf654692A27CC706a8783D9E226ab7998 +... +... +✅ [Success]Hash: 0x51b8707426039e2fb4f9464403176792dca80d94a4b7b8bf75d6fb1bddcb9144 +Contract Address: 0xdE663Eccf654692A27CC706a8783D9E226ab7998 +Block: 1505221 +Paid: 0.000455622 ETH (4556220 gas * 0.1 gwei) +``` + +🌟 Brilliant! Your contract factory and its first crowdfunding campaign are now operational. diff --git a/content/10.quick-start/_deploy_factory/_hardhat_deploy_contract_factory.md b/content/10.quick-start/_deploy_factory/_hardhat_deploy_contract_factory.md new file mode 100644 index 00000000..974005f5 --- /dev/null +++ b/content/10.quick-start/_deploy_factory/_hardhat_deploy_contract_factory.md @@ -0,0 +1,221 @@ +--- +title: Hardhat | Deploy Contract Factory +--- + +Hardhat is an Ethereum development environment, designed for easy smart contract +development in Solidity. zkSync provides its own plugins which makes working with +contracts on zkSync simple and efficient. + +## Step 1: Setting up environment +:display-partial{partial = "Environment Setup with zkSync CLI"} + +## Step 2: Set up wallet + +Deploying contracts on the zkSync Sepolia testnet requires having testnet ETH. +If you're working within the local development environment, +you can utilize pre-configured rich wallets and skip this step. +For testnet deployments, follow these steps to secure your funds: + +:display-partial{partial = "Setting up your wallet"} + +## Step 3: Deploying your first contract + +With our environment and wallet configured, we're set to review the `CrowdfundingFactory.sol` +contract that is provided during the initialization step in the `/contracts` directory. +The `CrowdfundingFactory.sol`contract will be used to deploy multiple instances of +the `CrowdfundingCampaign.sol` contract from the previous guide. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Crowdfunding campaign contract +import "./CrowdfundingCampaign.sol"; + +// Factory contract to create and manage crowdfunding campaigns +contract CrowdfundingFactory { + CrowdfundingCampaign[] public campaigns; + + event CampaignCreated(address campaignAddress, uint256 fundingGoal); + + function createCampaign(uint256 fundingGoal) public { + CrowdfundingCampaign newCampaign = new CrowdfundingCampaign(fundingGoal); + campaigns.push(newCampaign); + + emit CampaignCreated(address(newCampaign), fundingGoal); + } + + function getCampaigns() public view returns (CrowdfundingCampaign[] memory) { + return campaigns; + } +} +``` + +The `CrowdfundingFactory` contract automates the creation and oversight of +`CrowdfundingCampaign` contracts, each with its distinct funding goals, it features: + +- **Campaign Creation**: Utilizes the `createCampaign` method to initiate a new +`CrowdfundingCampaign` contract. This function takes a `fundingGoal` as an argument, +deploys a new campaign contract with this goal, and tracks the created campaign in the +`campaigns` array. +- **Campaign Tracking**: The `getCampaigns` method offers a view into all the campaigns +created by the factory, allowing for easy access and management of multiple crowdfunding +initiatives. + +This contract factory approach streamlines the deployment of crowdfunding campaigns, +making it efficient to launch and manage multiple campaigns concurrently. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +::code-group + +```bash [yarn] +yarn compile +``` + +```bash [pnpm] +pnpm run compile +``` + +```bash [npm] +npm run compile +``` + +```bash [bun] +bun run compile +``` + +:: + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +Compiling contracts for zkSync Era with zksolc v1.4.0 and solc v0.8.17 +Compiling 2 Solidity file +Successfully compiled 2 Solidity file +``` + +The compiled artifacts will be located in the `/artifacts-zk` folder. + +### Deploy + +This section outlines the steps to deploy the `CrowdfundingCampaign` contract +using our new `CrowdfundingFactory`. + +The deployment script is located at `/deploy/deployUsingFactory.ts`. + +```typescript +import { deployContract, getWallet } from "./utils"; +import { ethers } from "ethers"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +export default async function (hre: HardhatRuntimeEnvironment) { + const contractArtifactName = "CrowdfundingFactory"; + const constructorArguments = []; + const crowdfundingFactory = await deployContract(contractArtifactName, constructorArguments); + + console.log(`🏭 CrowdfundingFactory address: ${crowdfundingFactory.target}`); + + const contractArtifact = await hre.artifacts.readArtifact("CrowdfundingFactory"); + const factoryContract = new ethers.Contract( + crowdfundingFactory.target, + contractArtifact.abi, + getWallet() + ); + + // Define funding goal for the campaign, e.g., 0.1 ether + const fundingGoalInWei = ethers.parseEther('0.1').toString(); + + // Use the factory to create a new CrowdfundingCampaign + const createTx = await factoryContract.createCampaign(fundingGoalInWei); + await createTx.wait(); + + // Retrieve the address of the newly created CrowdfundingCampaign + const campaigns = await factoryContract.getCampaigns(); + const newCampaignAddress = campaigns[campaigns.length - 1]; + + console.log(`🚀 New CrowdfundingCampaign deployed at: ${newCampaignAddress}`); + console.log('✅ Deployment and campaign creation complete!'); +} +``` + +Key Components: + +**Deployment Workflow:** + +- Initiates by deploying the `CrowdfundingFactory` through `deployContract`. +- Using the factory's deployed address to form a `factoryContract` instance, +facilitating access to the factory's functionalities. + +**Initiating Campaigns:** + +- Executes the `factoryContract`'s `createCampaign` function to create and deploy a new +crowdfunding campaign, with the specified funding target. + +#### Deploy factory + +Execute the deployment command corresponding to your package manager. The default command +deploys to the configured network in your Hardhat setup. For local deployment, append +`--network inMemoryNode` to deploy to the local in-memory node running. + +::code-group + +```bash [yarn] +yarn hardhat deploy-zksync --script deployUsingFactory.ts +# To deploy the contract on local in-memory node: +# yarn hardhat deploy-zksync --script deployUsingFactory.ts --network inMemoryNode +``` + +```bash [pnpm] +pnpm run hardhat deploy-zksync --script deployUsingFactory.ts +# To deploy the contract on local in-memory node: +# pnpm run hardhat deploy-zksync --script deployUsingFactory.ts --network inMemoryNode +``` + +```bash [npm] +npm run hardhat deploy-zksync --script deployUsingFactory.ts +# To deploy the contract on local in-memory node: +# npm run hardhat deploy-zksync --script deployUsingFactory.ts --network inMemoryNode +``` + +```bash [bun] +bun run hardhat deploy-zksync --script deployUsingFactory.ts +# To deploy the contract on local in-memory node: +# bun run hardhat deploy-zksync --script deployUsingFactory.ts --network inMemoryNode +``` + +:: + +#### Expected Output + +Upon successful deployment, you'll receive output detailing the deployment process, +including the contract address, source, and encoded constructor arguments: + +```bash +Starting deployment process of "CrowdfundingFactory"... +Estimated deployment cost: 0.0002500236 ETH + +"CrowdfundingFactory" was successfully deployed: + - Contract address: 0xD084EF36f8F5353f70498cD84cb8D2B844C120a8 + - Contract source: contracts/CrowdfundFactory.sol:CrowdfundingFactory + - Encoded constructor arguments: 0x + +Requesting contract verification... +Your verification ID is: 10097 +Contract successfully verified on zkSync block explorer! +🏭 CrowdfundingFactory address: 0xD084EF36f8F5353f70498cD84cb8D2B844C120a8 +🚀 New CrowdfundingCampaign deployed at: 0x060B748eC3512795E94045c406CFd5877DD84e4D +✅ Deployment and campaign creation complete! +``` + +🌟 Brilliant! Your contract factory and its first crowdfunding campaign are now +operational. diff --git a/content/10.quick-start/_index/_foundry_deploy_contract.md b/content/10.quick-start/_index/_foundry_deploy_contract.md index 2aafc84d..2df26237 100644 --- a/content/10.quick-start/_index/_foundry_deploy_contract.md +++ b/content/10.quick-start/_index/_foundry_deploy_contract.md @@ -109,7 +109,7 @@ of Solidity files compiled. ```bash [⠒] Compiling... -[⠃] Compiling 21 files with 0.8.20 +[⠃] Compiling 2 files with 0.8.20 [⠊] Solc 0.8.20 finished in 736.48ms Compiler run successful! Compiling contracts for zkSync Era with zksolc v1.4.0 @@ -148,6 +148,8 @@ Key Components: - **contractArtifactName:** Identifies the `CrowdfundingCampaign` contract for deployment. - **constructorArguments:** Sets initialization parameters for the contract. +#### Deploy contract + Execute the deployment command. ```bash @@ -162,32 +164,7 @@ Upon successful deployment, you'll receive output detailing the deployment proce including the contract address, transaction hash, and block number deployed to: ```bash -Script ran successfully. - -## Setting up 1 EVM. - -========================== - -Chain 300 - -Estimated gas price: .1 gwei - -Estimated total gas used for script: 481392 - -Estimated amount required: 0.00015404544 ETH - -========================== - -### -Finding wallets for all the necessary addresses... -## -Sending transactions [0 - 0]. -⠁ [00:00:00] [###########################################################################################################################################################################################################] 1/1 txes (0.0s) - -## -Waiting for receipts. -⠉ [00:00:06] [#######################################################################################################################################################################################################] 1/1 receipts (0.0s) -##### 300 +... ✅ [Success]Hash: 0x69f5f1f0f5b3fa12ed2fbab4d6bb6edc02bbfff2f8c414d8171cc8295250296c Contract Address: 0xB0C0d3d02c270b6ABe4862EA90bBa1Af192314a8 Block: 1491370 diff --git a/content/10.quick-start/_index/_hardhat_deploy_contract.md b/content/10.quick-start/_index/_hardhat_deploy_contract.md index de126ffb..6ab8c0e6 100644 --- a/content/10.quick-start/_index/_hardhat_deploy_contract.md +++ b/content/10.quick-start/_index/_hardhat_deploy_contract.md @@ -147,6 +147,7 @@ Key Components: - **constructorArguments:** Sets initialization parameters for the contract. In this case, the fundraising goal, converted from ether to `wei` to match Solidity's `uint256` type. +#### Deploy contract Execute the deployment command corresponding to your package manager. The default command deploys to the configured network in your Hardhat setup. For local deployment, append `--network inMemoryNode` to deploy to the local in-memory node running. diff --git a/content/10.quick-start/_testing/_foundry_contract_testing.md b/content/10.quick-start/_testing/_foundry_contract_testing.md new file mode 100644 index 00000000..ae788953 --- /dev/null +++ b/content/10.quick-start/_testing/_foundry_contract_testing.md @@ -0,0 +1,216 @@ +--- +title: Hardhat | Contract Testing +--- + +`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. +It extends Foundry's capabilities for Ethereum app development to support zkSync, +allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. + +::callout{icon="i-heroicons-information-circle-16-solid" color="amber"} +`foundry-zksync` is still in an alpha stage, so some features might not be fully supported +yet and may not work as fully intended. It is open-sourced and contributions are welcomed. +:: + +## Step 1: Environment Configuration + +## Step 2: Test Wallet Configuration + +## Step 3: Testing `CrowdfundingCampaign` contract + +Now that our setup is complete, it's time to focus on the core of this +guide - testing our `CrowdfundingCampaign.sol` contract. Here's a quick +refresher on its structure: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract CrowdfundingCampaign { + address public owner; + uint256 public fundingGoal; + uint256 public totalFundsRaised; + mapping(address => uint256) public contributions; + + event ContributionReceived(address contributor, uint256 amount); + event GoalReached(uint256 totalFundsRaised); + + constructor(uint256 _fundingGoal) { + owner = msg.sender; + fundingGoal = _fundingGoal; + } + + function contribute() public payable { + require(msg.value > 0, "Contribution must be greater than 0"); + contributions[msg.sender] += msg.value; + totalFundsRaised += msg.value; + + emit ContributionReceived(msg.sender, msg.value); + + if (totalFundsRaised >= fundingGoal) { + emit GoalReached(totalFundsRaised); + } + } + + function withdrawFunds() public { + require(msg.sender == owner, "Only the owner can withdraw funds"); + require(totalFundsRaised >= fundingGoal, "Funding goal not reached"); + + uint256 amount = address(this).balance; + totalFundsRaised = 0; + + (bool success, ) = payable(owner).call{value: amount}(""); + require(success, "Transfer failed."); + } + + function getTotalFundsRaised() public view returns (uint256) { + return totalFundsRaised; + } + + function getFundingGoal() public view returns (uint256) { + return fundingGoal; + } +} +``` + +Thorough testing involves scrutinizing every function and aspect of our contract, +including potential failure scenarios. In this guide, we'll focus in on the `contribute` +method to ensure it's tested. + +As a challenge to hone your testing skills further, +consider devising additional tests for the `withdrawFunds`, `getTotalFundsRaised`, +and `getFundingGoal` methods, expanding your test coverage and reinforcing the +reliability of the contract. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +```bash +forge build --zksync +``` + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +[⠒] Compiling... +[⠃] Compiling 22 files with 0.8.20 +[⠊] Solc 0.8.20 finished in 736.48ms +Compiler run successful! +Compiling contracts for zkSync Era with zksolc v1.4.0 +``` + +The compiled zkEVM artifacts will be located in the `/zkout` folder, and the solc artifacts will be +located in the `/out` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/test` directory, specifically the +`CrowdfundingCampaign.t.sol` file. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../src/CrowdfundingCampaign.sol"; + +contract CrowdfundingCampaignTest is Test { + CrowdfundingCampaign campaign; + event GoalReached(uint256 totalFundsRaised); + address owner; + address addr1; + address addr2; + + function setUp() public { + owner = address(this); + + addr1 = vm.addr(1); + addr2 = vm.addr(2); + + campaign = new CrowdfundingCampaign(1 ether); + console.log("CrowdfundingCampaign deployed at: %s", address(campaign)); + } + + function test_RejectZeroContributions() public { + vm.expectRevert("Contribution must be greater than 0"); + campaign.contribute{value: 0}(); + } + + function test_AggregateContributions() public { + uint256 initialTotal = campaign.getTotalFundsRaised(); + + vm.prank(addr1); + vm.deal(addr1, 2 ether); + campaign.contribute{value: 0.5 ether}(); + + vm.prank(addr2); + vm.deal(addr2, 2 ether); + campaign.contribute{value: 0.3 ether}(); + + assertEq(campaign.getTotalFundsRaised(), initialTotal + 0.8 ether); + } + + function test_EmitGoalReachedWhenFundingGoalMet() public { + vm.prank(addr1); + vm.deal(addr1, 2 ether); + vm.expectEmit(true, true, false, true); + emit GoalReached(1 ether); + campaign.contribute{value: 1 ether}(); + } +} + +``` + +**Testing Workflow:** + +- **Environment Setup**: Leverages Foundry's `Test` contract and setup functions +to prepare the test environment, ensuring a fresh state for each test case. +- **Deployment and Address Simulation**: Deploys the `CrowdfundingCampaign` contract +within the test setup and simulates addresses using Foundry's `vm.addr()` function for +various test actors. + +**`contribute` Method Tests:** + +- **Zero Contribution Validation**: Asserts that the contract rejects contribution +attempts with zero value, testing the contract's input validation logic. +- **Contribution Aggregation**: Confirms the contract's ability to correctly tally +contributions from various addresses, ensuring accurate tracking of the total funds raised. +- **Event Emission Upon Goal Achievement**: Utilizes Foundry's `vm.expectEmit` to +anticipate the `GoalReached` event when the funding goal is met, validating the +contract's event logic and state transitions. + +#### Execute tests + +Execute the test command: + +```bash +forge test --zksync +``` + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash +Ran 3 tests for test/CrowdfundingCampaign.t.sol:CrowdfundingCampaignTest +[PASS] test_AggregateContributions() (gas: 29204) +[PASS] test_EmitGoalReachedWhenFundingGoalMet() (gas: 18862) +[PASS] test_RejectZeroContributions() (gas: 8148) +Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 44.03ms (43.94ms CPU time) + +Ran 1 test suite in 48.11ms (44.03ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. diff --git a/content/10.quick-start/_testing/_hardhat_contract_testing.md b/content/10.quick-start/_testing/_hardhat_contract_testing.md new file mode 100644 index 00000000..0a06a583 --- /dev/null +++ b/content/10.quick-start/_testing/_hardhat_contract_testing.md @@ -0,0 +1,263 @@ +--- +title: Hardhat | Contract Testing +--- + +Hardhat is an Ethereum development environment, designed for easy smart contract development in Solidity. +zkSync provides its own plugins which makes working with contracts on zkSync simple and efficient. + +## Step 1: Environment Configuration +While setting up a local development environment was previously optional, testing contracts requires +a more structured setup. We'll employ `hardhat-zksync` to run tests against an In-memory node, +which operates seamlessly within a separate process for an optimized testing workflow. +Kickstart the project setup for this guide with the following command: + + +```sh +npx zksync-cli@latest —template zksync-quickstart +``` + +Within the `hardhat.config.ts`, you'll observe the `zksync` flag set to `true` under the +`hardhat` network, indicating the integration with zkSync's testing environment. + +```ts + hardhat: { + zksync: true, + }, +``` + +To use the In-memory node for testing, ensure the `hardhat` network is selected with +the `zksync` flag enabled. This setup initiates the node alongside your tests and ensures +it terminates once testing is complete. The node's port allocation starts at the default +`8011`, facilitating smooth and isolated test execution. + +Secondly within the `hardhat.config.ts`, you'll observe the importing of +`@nomicfoundation/hardhat-chai-matchers`. This plugin provides Hardhat with an extended +suite of assertion methods tailored for contract testing, significantly improving the testing +toolkit available for your project. + +```typescript +import "@nomicfoundation/hardhat-chai-matchers"; +``` + +## Step 2: Test Wallet Configuration + +For testing purposes, we have the luxury of using pre-configured, well-funded wallets. +Throughout this testing guide, we will leverage the following pre-configured wallet, +eliminating the need for manual funding or setup: + +| Account Address | Private Key | +| -------------------------------------------- | -------------------------------------------------------------------- | +| `0x36615Cf349d7F6344891B1e7CA7C72883F5dc049` | `0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110` | + +This streamlined approach allows us to focus on writing and running effective tests. + +## Step 3: Testing `CrowdfundingCampaign` contract + +Now that our setup is complete, it's time to focus on the core of this +guide - testing our `CrowdfundingCampaign.sol` contract. Here's a quick +refresher on its structure: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract CrowdfundingCampaign { + address public owner; + uint256 public fundingGoal; + uint256 public totalFundsRaised; + mapping(address => uint256) public contributions; + + event ContributionReceived(address contributor, uint256 amount); + event GoalReached(uint256 totalFundsRaised); + + constructor(uint256 _fundingGoal) { + owner = msg.sender; + fundingGoal = _fundingGoal; + } + + function contribute() public payable { + require(msg.value > 0, "Contribution must be greater than 0"); + contributions[msg.sender] += msg.value; + totalFundsRaised += msg.value; + + emit ContributionReceived(msg.sender, msg.value); + + if (totalFundsRaised >= fundingGoal) { + emit GoalReached(totalFundsRaised); + } + } + + function withdrawFunds() public { + require(msg.sender == owner, "Only the owner can withdraw funds"); + require(totalFundsRaised >= fundingGoal, "Funding goal not reached"); + + uint256 amount = address(this).balance; + totalFundsRaised = 0; + + (bool success, ) = payable(owner).call{value: amount}(""); + require(success, "Transfer failed."); + } + + function getTotalFundsRaised() public view returns (uint256) { + return totalFundsRaised; + } + + function getFundingGoal() public view returns (uint256) { + return fundingGoal; + } +} +``` + +Thorough testing involves scrutinizing every function and aspect of our contract, +including potential failure scenarios. In this guide, we'll focus in on the `contribute` +method to ensure it's tested. + +As a challenge to hone your testing skills further, +consider devising additional tests for the `withdrawFunds`, `getTotalFundsRaised`, +and `getFundingGoal` methods, expanding your test coverage and reinforcing the +reliability of the contract. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +::code-group + +```bash [yarn] +yarn compile +``` + +```bash [pnpm] +pnpm run compile +``` + +```bash [npm] +npm run compile +``` + +```bash [bun] +bun run compile +``` + +:: + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +Compiling contracts for zkSync Era with zksolc v1.4.0 and solc v0.8.17 +Compiling 2 Solidity file +Successfully compiled 2 Solidity file +``` + +The compiled artifacts will be located in the `/artifacts-zk` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/tests` directory, specifically the +`crowdFunding.test.ts` file. + +```typescript +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { getWallet, LOCAL_RICH_WALLETS, deployContract } from "../deploy/utils"; + +describe("CrowdfundingCampaign", function () { + let campaign; + let owner, addr1, addr2; + + beforeEach(async function () { + owner = getWallet(LOCAL_RICH_WALLETS[0].privateKey); + addr1 = getWallet(LOCAL_RICH_WALLETS[1].privateKey); + addr2 = getWallet(LOCAL_RICH_WALLETS[2].privateKey); + const fundingGoalInWei = ethers.parseEther('1').toString(); + campaign = await deployContract("CrowdfundingCampaign", [fundingGoalInWei], { wallet: owner, silent: true }); + }); + + describe("Contribute", function () { + it("should reject contributions of 0", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("0") })).to.be.revertedWith("Contribution must be greater than 0"); + }); + + it("should aggregate contributions in totalFundsRaised", async function () { + await campaign.connect(addr1).contribute({ value: ethers.parseEther("0.5") }); + await campaign.connect(addr2).contribute({ value: ethers.parseEther("0.3") }); + expect(await campaign.getTotalFundsRaised()).to.equal(ethers.parseEther("0.8")); + }); + + it("should emit GoalReached event when funding goal is met", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("1") })) + .to.emit(campaign, "GoalReached") + .withArgs(ethers.parseEther("1")); + }); + }); +}); +``` + +Key Components: + +**Testing Workflow:** + +- **Initialization**: Each test case initializes with fresh contract instances and predefined +rich wallet accounts to simulate various contributors and the contract owner. +- **Deployment**: The `CrowdfundingCampaign` contract is deployed using the `deployContract` +utility, setting a specific funding goal for each test scenario. + +**`contribute` Method Tests:** + +- **Zero Contributions**: Verifies that the contract correctly rejects contribution attempts with +zero value, ensuring the integrity of the contribution process. +- **Funds Aggregation**: Tests the contract's ability to accurately aggregate contributions from +multiple addresses and update the `totalFundsRaised` accordingly. +- **Goal Achievement**: Checks for the `GoalReached` event emission upon meeting the funding goal, +confirming the contract's responsiveness to achieving its set target. + +#### Execute tests +Execute the test command corresponding to your package manager: + +::code-group + +```bash [yarn] +yarn hardhat test --network hardhat +``` + +```bash [pnpm] +pnpm run hardhat test --network hardhat +``` + +```bash [npm] +npm run hardhat test --network hardhat +``` + +```bash [bun] +bun run hardhat test --network hardhat +``` + +:: + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash + CrowdfundingCampaign + Contribute + ✔ should reject contributions of 0 (45ms) + ✔ should aggregate contributions in totalFundsRaised (213ms) + ✔ should emit GoalReached event when funding goal is met (113ms) + + + 3 passing (1s) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. From 2bda31637f57badc1bab08dd351e3925f32085fd Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Mon, 8 Apr 2024 19:37:04 -0500 Subject: [PATCH 10/54] feat: update upgrading directories and content --- ....deploy-factory.md => 2.deploy_factory.md} | 0 content/10.quick-start/3.testing.md | 41 +-- content/10.quick-start/4.upgrading.md | 185 +++--------- .../_foundry_beacon_contract_upgradability.md | 216 ++++++++++++++ .../_hardhat_beacon_contract_upgradability.md | 272 ++++++++++++++++++ .../_beacon_proxy_contract_upgradability.md | 32 +++ ...ndry_transparent_contract_upgradability.md | 216 ++++++++++++++ ...dhat_transparent_contract_upgradability.md | 272 ++++++++++++++++++ ...ransparent_proxy_contract_upgradability.md | 35 +++ .../_foundry_uups_contract_upgradability.md | 216 ++++++++++++++ .../_hardhat_uups_contract_upgradability.md | 272 ++++++++++++++++++ .../_uups_contract_upgradability.md | 33 +++ cspell-config/cspell-blockchain.txt | 4 + cspell-config/cspell-dev.txt | 1 + 14 files changed, 1636 insertions(+), 159 deletions(-) rename content/10.quick-start/{2.deploy-factory.md => 2.deploy_factory.md} (100%) create mode 100644 content/10.quick-start/_upgrading/_beacon/_foundry_beacon_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_beacon/_hardhat_beacon_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_beacon_proxy_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_transparent/_foundry_transparent_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_transparent/_hardhat_transparent_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_transparent_proxy_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_uups/_foundry_uups_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_uups/_hardhat_uups_contract_upgradability.md create mode 100644 content/10.quick-start/_upgrading/_uups_contract_upgradability.md diff --git a/content/10.quick-start/2.deploy-factory.md b/content/10.quick-start/2.deploy_factory.md similarity index 100% rename from content/10.quick-start/2.deploy-factory.md rename to content/10.quick-start/2.deploy_factory.md diff --git a/content/10.quick-start/3.testing.md b/content/10.quick-start/3.testing.md index 72caa9f8..f8df6cae 100644 --- a/content/10.quick-start/3.testing.md +++ b/content/10.quick-start/3.testing.md @@ -35,21 +35,26 @@ items: [{ ## Takeaways -- **EVM Compatibility:** zkSync is EVM compatible and you can write smart contracts in Solidity or Vyper. -- **Development Tools:** zkSync supports your favorite development toolkit Hardhat and Foundry. -- **Custom Compilation:** Contracts deployed to zkSync are compiled using `zksolc` or `zkvyper` as -they generate a special bytecode for zkSync's ZKEVM. - -## Next steps - -Having successfully deployed your first contract on zkSync, you're well on your way to becoming -a proficient zkSync developer. To expand your expertise: - -- **Explore Contract Factories:** Enhance your project by building a contract factory -for the `CrowdfundingCampaign` contract in the next guide. This will allow you to efficiently -manage multiple crowdfunding campaigns, each with its own unique parameters. -- **Dive Deeper into zkSync Features:** Investigate advanced zkSync features such as account abstraction, -and paymasters. -- **Join the Community:** Engage with the zkSync developer community through forums, -Discord channels, Dev Discussions, or GitHub repositories. Share your experiences, ask questions, -and collaborate on projects. +- **Testing**: Understanding contract testing is important for ensuring the reliability and security of your smart contracts +on zkSync. Proper testing safeguards against unforeseen errors and vulnerabilities. +- **Comprehensive Coverage**: Achieving comprehensive test coverage, including both positive and negative testing +scenarios, is essential for a robust smart contract. This guide emphasized the `contribute` method, +but testing should encompass all aspects of your contract. +- **Tooling Efficiency**: Leveraging Hardhat or Foundry for testing provides a streamlined and efficient workflow. +These tools offer powerful features and plugins, like `@nomicfoundation/hardhat-chai-matchers`, +that enhance the testing process. + +## Next Steps + +With a solid foundation in contract testing, you're well-equipped to advance your zkSync +development journey. Consider the following steps to further your expertise: + +- **Upgradeability**: Delve into the next guide focusing on contract upgradability. +Learning to make your contracts upgradeable will enable you to update and improve your smart contracts +over time without losing state or funds. +- **Advanced zkSync Integrations:** Explore deeper into zkSync's ecosystem by +implementing features like account abstraction and paymasters to enhance user +experience and contract flexibility. +- **Community Engagement and Contribution:** Join the vibrant zkSync community. +Participate in forums, Discord, or GitHub discussions. Sharing insights, asking queries, +and contributing can enrich the ecosystem and your understanding of zkSync. diff --git a/content/10.quick-start/4.upgrading.md b/content/10.quick-start/4.upgrading.md index 8c168d38..6143c9ed 100644 --- a/content/10.quick-start/4.upgrading.md +++ b/content/10.quick-start/4.upgrading.md @@ -1,155 +1,58 @@ --- -title: Upgradeability -description: Learn how to write and customize your documentation. +title: Upgrading Contracts +description: Learn to make smart contracts upgradeable within the zkSync ecosystem. --- -This is only a basic example of what you can achieve with [Nuxt UI Pro](https://ui.nuxt.com/pro/guide), you can tweak it -to match your needs. The template uses several Nuxt modules underneath like [`@nuxt/content`](https://content.nuxt.com) -for the content, [`@nuxtjs/fontaine`](https://github.com/nuxt-modules/fontaine) and -[`@nuxtjs/google-fonts`](https://github.com/nuxt-modules/google-fonts) to change the font and -[`nuxt-og-image`](https://nuxtseo.com/og-image/getting-started/installation) for social previews. +Welcome back to our Quickstart Series, the express lane to zkSync development! +In this fourth installment, we embark on a journey through contract upgradability, +an important aspect for maintaining and enhancing smart contracts over time. This guide will +lead you through the strategies and practices for making the `CrowdfundingCampaign` contract, +introduced in the first guide and brought to life in subsequent guides, **upgradeable**. -::callout +:check-icon Harnessing advanced techniques for contract upgradability in zkSync. -icon: i-heroicons-light-bulb target: \_blank to: [nuxt guide](https://ui.nuxt.com/pro/guide/usage#structure) +:check-icon Implementing upgradeable patterns for the `CrowdfundingCampaign` to ensure long-term adaptability and improvement. ---- - -Learn more on how to customize and structure a Nuxt UI Pro app! :: - -## Writing content - -You can just start writing `.md` or `.yml` files in the [`content/`](https://content.nuxt.com/usage/content-directory) -directory to have your pages updated. The navigation will be automatically generated in the left aside and in the mobile -menu. You will also be able to go through your content with full-text search. - -::callout{icon="i-heroicons-light-bulb"} This template relies on a -[catch-all route](https://nuxt.com/docs/guide/directory-structure/pages#catch-all-route) but you can achieve the same -thing with the [document-driven mode](https://content.nuxt.com/document-driven/introduction). :: - -## App Configuration +:check-icon Leveraging tools and best practices in zkSync to facilitate seamless contract upgrades. -In addition to `@nuxt/ui-pro` configuration through the `app.config.ts`, this template lets you customize the `Header`, -`Footer` and the `Table of contents` components. +Begin to understand smart contract evolution and empower your zkSync applications with the +flexibility of upgradability. -### Header +### Select preferred upgrade mechanism -```ts [app.config.ts] -export default defineAppConfig({ - header: { - // Logo configuration - logo: { - // Light mode - light: { - src: '', - alt: '', - class: '', - }, - // Dark mode - dark: { - src: '', - alt: '', - class: '', - }, - }, - // Show or hide the search bar - search: true, - // Show or hide the color mode button - colorMode: true, - // Customize links - links: [ - { - icon: 'i-simple-icons-github', - to: 'https://github.com/nuxt-ui-pro/docs', - target: '_blank', - 'aria-label': 'Docs template on GitHub', - }, - ], - }, -}); -``` - -### Footer - -```ts [app.config.ts] -export default defineAppConfig({ - footer: { - // Update bottom left credits - credits: 'Copyright © 2023', - // Show or hide the color mode button - colorMode: false, - // Customize links - links: [ - { - icon: 'i-simple-icons-nuxtdotjs', - to: 'https://nuxt.com', - target: '_blank', - 'aria-label': 'Nuxt Website', - }, - { - icon: 'i-simple-icons-discord', - to: 'https://discord.com/invite/ps2h6QT', - target: '_blank', - 'aria-label': 'Nuxt UI on Discord', - }, - { - icon: 'i-simple-icons-x', - to: 'https://x.com/nuxt_js', - target: '_blank', - 'aria-label': 'Nuxt on X', - }, - { - icon: 'i-simple-icons-github', - to: 'https://github.com/nuxt/ui', - target: '_blank', - 'aria-label': 'Nuxt UI on GitHub', - }, - ], - }, -}); -``` +::content-switcher +--- +items: [{ + label: 'Transparent', + partial: '_upgrading/_transparent_proxy_contract_upgradability' +}, { + label: 'Beacon', + partial: '_upgrading/_beacon_proxy_contract_upgradability' +}, + { + label: 'UUPS', + partial: '_upgrading/_uups_contract_upgradability' +},] +--- +:: -### Table of contents +## Takeaways -```ts [app.config.ts] -export default defineAppConfig({ - toc: { - // Title of the main table of contents - title: 'Table of Contents', - // Bottom TOC configuration - bottom: { - // Title of the bottom table of contents - title: 'Community', - // URL of your repository content folder - edit: '', - // Customize links - links: [ - { - icon: 'i-heroicons-star', - label: 'Star on GitHub', - to: 'https://github.com/nuxt/ui', - target: '_blank', - }, - { - icon: 'i-heroicons-book-open', - label: 'Nuxt UI Pro docs', - to: 'https://ui.nuxt.com/pro/guide', - target: '_blank', - }, - { - icon: 'i-simple-icons-nuxtdotjs', - label: 'Purchase a license', - to: 'https://ui.nuxt.com/pro/purchase', - target: '_blank', - }, - ], - }, - }, -}); -``` +- **EVM Compatibility:** zkSync is EVM compatible and you can write smart contracts in Solidity or Vyper. +- **Development Tools:** zkSync supports your favorite development toolkit Hardhat and Foundry. +- **Custom Compilation:** Contracts deployed to zkSync are compiled using `zksolc` or `zkvyper` as +they generate a special bytecode for zkSync's ZKEVM. -icon: i-heroicons-light-bulb target: \_blank to: [app-config](https://nuxt.studio/docs/developers/app-config) +## Next steps ---- +Having successfully deployed your first contract on zkSync, you're well on your way to becoming +a proficient zkSync developer. To expand your expertise: -A dedicated interface is provided to edit those configurations on Nuxt Studio. :: +- **Explore Contract Factories:** Enhance your project by building a contract factory +for the `CrowdfundingCampaign` contract in the next guide. This will allow you to efficiently +manage multiple crowdfunding campaigns, each with its own unique parameters. +- **Dive Deeper into zkSync Features:** Investigate advanced zkSync features such as account abstraction, +and paymasters. +- **Join the Community:** Engage with the zkSync developer community through forums, +Discord channels, Dev Discussions, or GitHub repositories. Share your experiences, ask questions, +and collaborate on projects. diff --git a/content/10.quick-start/_upgrading/_beacon/_foundry_beacon_contract_upgradability.md b/content/10.quick-start/_upgrading/_beacon/_foundry_beacon_contract_upgradability.md new file mode 100644 index 00000000..ae788953 --- /dev/null +++ b/content/10.quick-start/_upgrading/_beacon/_foundry_beacon_contract_upgradability.md @@ -0,0 +1,216 @@ +--- +title: Hardhat | Contract Testing +--- + +`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. +It extends Foundry's capabilities for Ethereum app development to support zkSync, +allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. + +::callout{icon="i-heroicons-information-circle-16-solid" color="amber"} +`foundry-zksync` is still in an alpha stage, so some features might not be fully supported +yet and may not work as fully intended. It is open-sourced and contributions are welcomed. +:: + +## Step 1: Environment Configuration + +## Step 2: Test Wallet Configuration + +## Step 3: Testing `CrowdfundingCampaign` contract + +Now that our setup is complete, it's time to focus on the core of this +guide - testing our `CrowdfundingCampaign.sol` contract. Here's a quick +refresher on its structure: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract CrowdfundingCampaign { + address public owner; + uint256 public fundingGoal; + uint256 public totalFundsRaised; + mapping(address => uint256) public contributions; + + event ContributionReceived(address contributor, uint256 amount); + event GoalReached(uint256 totalFundsRaised); + + constructor(uint256 _fundingGoal) { + owner = msg.sender; + fundingGoal = _fundingGoal; + } + + function contribute() public payable { + require(msg.value > 0, "Contribution must be greater than 0"); + contributions[msg.sender] += msg.value; + totalFundsRaised += msg.value; + + emit ContributionReceived(msg.sender, msg.value); + + if (totalFundsRaised >= fundingGoal) { + emit GoalReached(totalFundsRaised); + } + } + + function withdrawFunds() public { + require(msg.sender == owner, "Only the owner can withdraw funds"); + require(totalFundsRaised >= fundingGoal, "Funding goal not reached"); + + uint256 amount = address(this).balance; + totalFundsRaised = 0; + + (bool success, ) = payable(owner).call{value: amount}(""); + require(success, "Transfer failed."); + } + + function getTotalFundsRaised() public view returns (uint256) { + return totalFundsRaised; + } + + function getFundingGoal() public view returns (uint256) { + return fundingGoal; + } +} +``` + +Thorough testing involves scrutinizing every function and aspect of our contract, +including potential failure scenarios. In this guide, we'll focus in on the `contribute` +method to ensure it's tested. + +As a challenge to hone your testing skills further, +consider devising additional tests for the `withdrawFunds`, `getTotalFundsRaised`, +and `getFundingGoal` methods, expanding your test coverage and reinforcing the +reliability of the contract. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +```bash +forge build --zksync +``` + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +[⠒] Compiling... +[⠃] Compiling 22 files with 0.8.20 +[⠊] Solc 0.8.20 finished in 736.48ms +Compiler run successful! +Compiling contracts for zkSync Era with zksolc v1.4.0 +``` + +The compiled zkEVM artifacts will be located in the `/zkout` folder, and the solc artifacts will be +located in the `/out` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/test` directory, specifically the +`CrowdfundingCampaign.t.sol` file. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../src/CrowdfundingCampaign.sol"; + +contract CrowdfundingCampaignTest is Test { + CrowdfundingCampaign campaign; + event GoalReached(uint256 totalFundsRaised); + address owner; + address addr1; + address addr2; + + function setUp() public { + owner = address(this); + + addr1 = vm.addr(1); + addr2 = vm.addr(2); + + campaign = new CrowdfundingCampaign(1 ether); + console.log("CrowdfundingCampaign deployed at: %s", address(campaign)); + } + + function test_RejectZeroContributions() public { + vm.expectRevert("Contribution must be greater than 0"); + campaign.contribute{value: 0}(); + } + + function test_AggregateContributions() public { + uint256 initialTotal = campaign.getTotalFundsRaised(); + + vm.prank(addr1); + vm.deal(addr1, 2 ether); + campaign.contribute{value: 0.5 ether}(); + + vm.prank(addr2); + vm.deal(addr2, 2 ether); + campaign.contribute{value: 0.3 ether}(); + + assertEq(campaign.getTotalFundsRaised(), initialTotal + 0.8 ether); + } + + function test_EmitGoalReachedWhenFundingGoalMet() public { + vm.prank(addr1); + vm.deal(addr1, 2 ether); + vm.expectEmit(true, true, false, true); + emit GoalReached(1 ether); + campaign.contribute{value: 1 ether}(); + } +} + +``` + +**Testing Workflow:** + +- **Environment Setup**: Leverages Foundry's `Test` contract and setup functions +to prepare the test environment, ensuring a fresh state for each test case. +- **Deployment and Address Simulation**: Deploys the `CrowdfundingCampaign` contract +within the test setup and simulates addresses using Foundry's `vm.addr()` function for +various test actors. + +**`contribute` Method Tests:** + +- **Zero Contribution Validation**: Asserts that the contract rejects contribution +attempts with zero value, testing the contract's input validation logic. +- **Contribution Aggregation**: Confirms the contract's ability to correctly tally +contributions from various addresses, ensuring accurate tracking of the total funds raised. +- **Event Emission Upon Goal Achievement**: Utilizes Foundry's `vm.expectEmit` to +anticipate the `GoalReached` event when the funding goal is met, validating the +contract's event logic and state transitions. + +#### Execute tests + +Execute the test command: + +```bash +forge test --zksync +``` + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash +Ran 3 tests for test/CrowdfundingCampaign.t.sol:CrowdfundingCampaignTest +[PASS] test_AggregateContributions() (gas: 29204) +[PASS] test_EmitGoalReachedWhenFundingGoalMet() (gas: 18862) +[PASS] test_RejectZeroContributions() (gas: 8148) +Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 44.03ms (43.94ms CPU time) + +Ran 1 test suite in 48.11ms (44.03ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. diff --git a/content/10.quick-start/_upgrading/_beacon/_hardhat_beacon_contract_upgradability.md b/content/10.quick-start/_upgrading/_beacon/_hardhat_beacon_contract_upgradability.md new file mode 100644 index 00000000..f32448fe --- /dev/null +++ b/content/10.quick-start/_upgrading/_beacon/_hardhat_beacon_contract_upgradability.md @@ -0,0 +1,272 @@ +--- +title: Hardhat | Contract Upgrading +--- + +Hardhat is an Ethereum development environment, designed for easy smart contract development in Solidity. +zkSync provides its own plugins which makes working with contracts on zkSync simple and efficient. + +## Step 1: Environment Configuration + +## Step 2: Test Wallet Configuration + +## Step 3: Adapting `CrowdfundingCampaign.sol` contract for upgradeability + +To adapt our `CrowdfundingCampaign.sol` contract for upgradeability, we're +transitioning to a Transparent Proxy pattern. This approach separates the +contract's logic (which can be upgraded) from its persistent state +(stored in the proxy). + +**Refactoring for Proxy Compatibility:** + +We're refactoring the contract to initialize state variables through an +`initialize` function instead of the constructor, in line with the +Transparent Proxy pattern. + +**Updated Contract Structure:** + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +contract CrowdfundingCampaign is Initializable { + address public owner; + uint256 public fundingGoal; + uint256 public deadline; + + event ContributionReceived(address contributor, uint256 amount); + + function initialize(uint256 _fundingGoal, uint256 _duration) public initializer { + owner = msg.sender; + fundingGoal = _fundingGoal; + deadline = block.timestamp + _duration; + } + + function contribute() public payable { + // contribution logic remains the same + } + + function withdrawFunds() public { + // withdrawFunds logic remains the same + } + + function getTotalFundsRaised() public view returns (uint256) { + // getTotalFundsRaised remains the same + } + + function getFundingGoal() public view returns (uint256) { + // getFundingGoal remains the same + } +} +``` + +**Key Modifications:** + +- **Initializable**: Inherits from OpenZeppelin's `Initializable` to ensure the `initialize` function +can only be called once, similar to a constructor. +- **Initialize Function**: Replaces the constructor for setting initial state, facilitating upgrades +through new logic contracts. +- **Transparent Proxy Pattern**: Utilizes a proxy contract to delegate calls to this logic contract, +allowing for future upgrades without losing the contract's state. + +This restructuring not only prepares the `CrowdfundingCampaign` contract for upgradeability. + +## Step 4: Upgrading the `CrowdfundingCampaign` Contract + +With our initial setup in place, we're ready to enhance our `CrowdfundingCampaign.sol` +contract by incorporating a deadline for contributions. This addition not only brings +a new layer of functionality but also introduces the concept of time-based conditions +through a `modifier`. + +**Current Contract Overview:** + +The existing version of our contract allows for open-ended contributions towards a +funding goal, without any time constraints. + +**Proposed Upgrade:** + +We're introducing a `deadline` variable, initialized at contract deployment, to establish a +clear timeframe for accepting contributions. The `withinDeadline` modifier will then enforce +this constraint, ensuring contributions are made within the allowed period. + +**Enhanced Contract:** + +```solidity +uint256 public deadline; + +function initialize(uint256 _fundingGoal, uint256 _duration) public initializer { + owner = msg.sender; + fundingGoal = _fundingGoal; + deadline = block.timestamp + _duration; +} + +modifier withinDeadline() { + require(block.timestamp <= deadline, "Funding period has ended"); + _; +} + +function contribute() public payable withinDeadline { + // Existing contribution logic +} +``` + +**Deadline Extension Capability:** + +To provide flexibility, a new function allows the owner to extend the deadline, +offering adaptability to changing campaign needs. + +```solidity +function extendDeadline(uint256 _newDuration) public { + require(msg.sender == owner, "Only the owner can extend the deadline"); + deadline = block.timestamp + _newDuration; +} +``` + +This upgrade not only introduces the element of time to the campaign but also +exemplifies the use of `modifiers` for enforcing contract conditions. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +::code-group + +```bash [yarn] +yarn compile +``` + +```bash [pnpm] +pnpm run compile +``` + +```bash [npm] +npm run compile +``` + +```bash [bun] +bun run compile +``` + +:: + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +Compiling contracts for zkSync Era with zksolc v1.4.0 and solc v0.8.17 +Compiling 2 Solidity file +Successfully compiled 2 Solidity file +``` + +The compiled artifacts will be located in the `/artifacts-zk` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/tests` directory, specifically the +`crowdFunding.test.ts` file. + +```typescript +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { getWallet, LOCAL_RICH_WALLETS, deployContract } from "../deploy/utils"; + +describe("CrowdfundingCampaign", function () { + let campaign; + let owner, addr1, addr2; + + beforeEach(async function () { + owner = getWallet(LOCAL_RICH_WALLETS[0].privateKey); + addr1 = getWallet(LOCAL_RICH_WALLETS[1].privateKey); + addr2 = getWallet(LOCAL_RICH_WALLETS[2].privateKey); + const fundingGoalInWei = ethers.parseEther('1').toString(); + campaign = await deployContract("CrowdfundingCampaign", [fundingGoalInWei], { wallet: owner, silent: true }); + }); + + describe("Contribute", function () { + it("should reject contributions of 0", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("0") })).to.be.revertedWith("Contribution must be greater than 0"); + }); + + it("should aggregate contributions in totalFundsRaised", async function () { + await campaign.connect(addr1).contribute({ value: ethers.parseEther("0.5") }); + await campaign.connect(addr2).contribute({ value: ethers.parseEther("0.3") }); + expect(await campaign.getTotalFundsRaised()).to.equal(ethers.parseEther("0.8")); + }); + + it("should emit GoalReached event when funding goal is met", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("1") })) + .to.emit(campaign, "GoalReached") + .withArgs(ethers.parseEther("1")); + }); + }); +}); +``` + +Key Components: + +**Testing Workflow:** + +- **Initialization**: Each test case initializes with fresh contract instances and predefined +rich wallet accounts to simulate various contributors and the contract owner. +- **Deployment**: The `CrowdfundingCampaign` contract is deployed using the `deployContract` +utility, setting a specific funding goal for each test scenario. + +**`contribute` Method Tests:** + +- **Zero Contributions**: Verifies that the contract correctly rejects contribution attempts with +zero value, ensuring the integrity of the contribution process. +- **Funds Aggregation**: Tests the contract's ability to accurately aggregate contributions from +multiple addresses and update the `totalFundsRaised` accordingly. +- **Goal Achievement**: Checks for the `GoalReached` event emission upon meeting the funding goal, +confirming the contract's responsiveness to achieving its set target. + +#### Execute tests +Execute the test command corresponding to your package manager: + +::code-group + +```bash [yarn] +yarn hardhat test --network hardhat +``` + +```bash [pnpm] +pnpm run hardhat test --network hardhat +``` + +```bash [npm] +npm run hardhat test --network hardhat +``` + +```bash [bun] +bun run hardhat test --network hardhat +``` + +:: + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash + CrowdfundingCampaign + Contribute + ✔ should reject contributions of 0 (45ms) + ✔ should aggregate contributions in totalFundsRaised (213ms) + ✔ should emit GoalReached event when funding goal is met (113ms) + + + 3 passing (1s) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. diff --git a/content/10.quick-start/_upgrading/_beacon_proxy_contract_upgradability.md b/content/10.quick-start/_upgrading/_beacon_proxy_contract_upgradability.md new file mode 100644 index 00000000..c1b4b877 --- /dev/null +++ b/content/10.quick-start/_upgrading/_beacon_proxy_contract_upgradability.md @@ -0,0 +1,32 @@ +--- +title: Beacon Proxy Contract Upgradeability +--- + +### What is a beacon proxy upgradeable contract? +Beacon Proxy Upgradeable Contracts leverage a beacon to manage upgrades, allowing +for centralized logic updates across multiple proxies. The structure includes: + +1. **Beacon Contract**: Acts as the central point holding the address of the current logic contract. +It enables updating the logic for all associated proxies through a single transaction. +2. **Proxy Contracts**: These lightweight contracts delegate calls to the logic contract address +provided by the beacon, maintaining their own state and balance. +3. **Logic Contract**: Contains the executable business logic, which can be updated by changing +the beacon's reference without altering individual proxies. +4. **Admin Address**: Authorized to update the logic contract address in the beacon, ensuring controlled and secure upgrades. + +This arrangement allows multiple proxy contracts to be upgraded simultaneously by updating +the logic contract address in the beacon, streamlining the upgrade process. It preserves +the state and balance of each proxy contract, offering an efficient way to roll out new +features or fixes while maintaining a uniform interface for users. + +::content-switcher +--- +items: [{ + label: 'Hardhat', + partial: '_upgrading/_beacon/_hardhat_transparent_contract_upgradability' +}, { + label: 'Foundry', + partial: '_upgrading/_beacon/_foundry_transparent_contract_upgradability' +}] +--- +:: diff --git a/content/10.quick-start/_upgrading/_transparent/_foundry_transparent_contract_upgradability.md b/content/10.quick-start/_upgrading/_transparent/_foundry_transparent_contract_upgradability.md new file mode 100644 index 00000000..ae788953 --- /dev/null +++ b/content/10.quick-start/_upgrading/_transparent/_foundry_transparent_contract_upgradability.md @@ -0,0 +1,216 @@ +--- +title: Hardhat | Contract Testing +--- + +`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. +It extends Foundry's capabilities for Ethereum app development to support zkSync, +allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. + +::callout{icon="i-heroicons-information-circle-16-solid" color="amber"} +`foundry-zksync` is still in an alpha stage, so some features might not be fully supported +yet and may not work as fully intended. It is open-sourced and contributions are welcomed. +:: + +## Step 1: Environment Configuration + +## Step 2: Test Wallet Configuration + +## Step 3: Testing `CrowdfundingCampaign` contract + +Now that our setup is complete, it's time to focus on the core of this +guide - testing our `CrowdfundingCampaign.sol` contract. Here's a quick +refresher on its structure: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract CrowdfundingCampaign { + address public owner; + uint256 public fundingGoal; + uint256 public totalFundsRaised; + mapping(address => uint256) public contributions; + + event ContributionReceived(address contributor, uint256 amount); + event GoalReached(uint256 totalFundsRaised); + + constructor(uint256 _fundingGoal) { + owner = msg.sender; + fundingGoal = _fundingGoal; + } + + function contribute() public payable { + require(msg.value > 0, "Contribution must be greater than 0"); + contributions[msg.sender] += msg.value; + totalFundsRaised += msg.value; + + emit ContributionReceived(msg.sender, msg.value); + + if (totalFundsRaised >= fundingGoal) { + emit GoalReached(totalFundsRaised); + } + } + + function withdrawFunds() public { + require(msg.sender == owner, "Only the owner can withdraw funds"); + require(totalFundsRaised >= fundingGoal, "Funding goal not reached"); + + uint256 amount = address(this).balance; + totalFundsRaised = 0; + + (bool success, ) = payable(owner).call{value: amount}(""); + require(success, "Transfer failed."); + } + + function getTotalFundsRaised() public view returns (uint256) { + return totalFundsRaised; + } + + function getFundingGoal() public view returns (uint256) { + return fundingGoal; + } +} +``` + +Thorough testing involves scrutinizing every function and aspect of our contract, +including potential failure scenarios. In this guide, we'll focus in on the `contribute` +method to ensure it's tested. + +As a challenge to hone your testing skills further, +consider devising additional tests for the `withdrawFunds`, `getTotalFundsRaised`, +and `getFundingGoal` methods, expanding your test coverage and reinforcing the +reliability of the contract. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +```bash +forge build --zksync +``` + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +[⠒] Compiling... +[⠃] Compiling 22 files with 0.8.20 +[⠊] Solc 0.8.20 finished in 736.48ms +Compiler run successful! +Compiling contracts for zkSync Era with zksolc v1.4.0 +``` + +The compiled zkEVM artifacts will be located in the `/zkout` folder, and the solc artifacts will be +located in the `/out` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/test` directory, specifically the +`CrowdfundingCampaign.t.sol` file. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../src/CrowdfundingCampaign.sol"; + +contract CrowdfundingCampaignTest is Test { + CrowdfundingCampaign campaign; + event GoalReached(uint256 totalFundsRaised); + address owner; + address addr1; + address addr2; + + function setUp() public { + owner = address(this); + + addr1 = vm.addr(1); + addr2 = vm.addr(2); + + campaign = new CrowdfundingCampaign(1 ether); + console.log("CrowdfundingCampaign deployed at: %s", address(campaign)); + } + + function test_RejectZeroContributions() public { + vm.expectRevert("Contribution must be greater than 0"); + campaign.contribute{value: 0}(); + } + + function test_AggregateContributions() public { + uint256 initialTotal = campaign.getTotalFundsRaised(); + + vm.prank(addr1); + vm.deal(addr1, 2 ether); + campaign.contribute{value: 0.5 ether}(); + + vm.prank(addr2); + vm.deal(addr2, 2 ether); + campaign.contribute{value: 0.3 ether}(); + + assertEq(campaign.getTotalFundsRaised(), initialTotal + 0.8 ether); + } + + function test_EmitGoalReachedWhenFundingGoalMet() public { + vm.prank(addr1); + vm.deal(addr1, 2 ether); + vm.expectEmit(true, true, false, true); + emit GoalReached(1 ether); + campaign.contribute{value: 1 ether}(); + } +} + +``` + +**Testing Workflow:** + +- **Environment Setup**: Leverages Foundry's `Test` contract and setup functions +to prepare the test environment, ensuring a fresh state for each test case. +- **Deployment and Address Simulation**: Deploys the `CrowdfundingCampaign` contract +within the test setup and simulates addresses using Foundry's `vm.addr()` function for +various test actors. + +**`contribute` Method Tests:** + +- **Zero Contribution Validation**: Asserts that the contract rejects contribution +attempts with zero value, testing the contract's input validation logic. +- **Contribution Aggregation**: Confirms the contract's ability to correctly tally +contributions from various addresses, ensuring accurate tracking of the total funds raised. +- **Event Emission Upon Goal Achievement**: Utilizes Foundry's `vm.expectEmit` to +anticipate the `GoalReached` event when the funding goal is met, validating the +contract's event logic and state transitions. + +#### Execute tests + +Execute the test command: + +```bash +forge test --zksync +``` + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash +Ran 3 tests for test/CrowdfundingCampaign.t.sol:CrowdfundingCampaignTest +[PASS] test_AggregateContributions() (gas: 29204) +[PASS] test_EmitGoalReachedWhenFundingGoalMet() (gas: 18862) +[PASS] test_RejectZeroContributions() (gas: 8148) +Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 44.03ms (43.94ms CPU time) + +Ran 1 test suite in 48.11ms (44.03ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. diff --git a/content/10.quick-start/_upgrading/_transparent/_hardhat_transparent_contract_upgradability.md b/content/10.quick-start/_upgrading/_transparent/_hardhat_transparent_contract_upgradability.md new file mode 100644 index 00000000..f32448fe --- /dev/null +++ b/content/10.quick-start/_upgrading/_transparent/_hardhat_transparent_contract_upgradability.md @@ -0,0 +1,272 @@ +--- +title: Hardhat | Contract Upgrading +--- + +Hardhat is an Ethereum development environment, designed for easy smart contract development in Solidity. +zkSync provides its own plugins which makes working with contracts on zkSync simple and efficient. + +## Step 1: Environment Configuration + +## Step 2: Test Wallet Configuration + +## Step 3: Adapting `CrowdfundingCampaign.sol` contract for upgradeability + +To adapt our `CrowdfundingCampaign.sol` contract for upgradeability, we're +transitioning to a Transparent Proxy pattern. This approach separates the +contract's logic (which can be upgraded) from its persistent state +(stored in the proxy). + +**Refactoring for Proxy Compatibility:** + +We're refactoring the contract to initialize state variables through an +`initialize` function instead of the constructor, in line with the +Transparent Proxy pattern. + +**Updated Contract Structure:** + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +contract CrowdfundingCampaign is Initializable { + address public owner; + uint256 public fundingGoal; + uint256 public deadline; + + event ContributionReceived(address contributor, uint256 amount); + + function initialize(uint256 _fundingGoal, uint256 _duration) public initializer { + owner = msg.sender; + fundingGoal = _fundingGoal; + deadline = block.timestamp + _duration; + } + + function contribute() public payable { + // contribution logic remains the same + } + + function withdrawFunds() public { + // withdrawFunds logic remains the same + } + + function getTotalFundsRaised() public view returns (uint256) { + // getTotalFundsRaised remains the same + } + + function getFundingGoal() public view returns (uint256) { + // getFundingGoal remains the same + } +} +``` + +**Key Modifications:** + +- **Initializable**: Inherits from OpenZeppelin's `Initializable` to ensure the `initialize` function +can only be called once, similar to a constructor. +- **Initialize Function**: Replaces the constructor for setting initial state, facilitating upgrades +through new logic contracts. +- **Transparent Proxy Pattern**: Utilizes a proxy contract to delegate calls to this logic contract, +allowing for future upgrades without losing the contract's state. + +This restructuring not only prepares the `CrowdfundingCampaign` contract for upgradeability. + +## Step 4: Upgrading the `CrowdfundingCampaign` Contract + +With our initial setup in place, we're ready to enhance our `CrowdfundingCampaign.sol` +contract by incorporating a deadline for contributions. This addition not only brings +a new layer of functionality but also introduces the concept of time-based conditions +through a `modifier`. + +**Current Contract Overview:** + +The existing version of our contract allows for open-ended contributions towards a +funding goal, without any time constraints. + +**Proposed Upgrade:** + +We're introducing a `deadline` variable, initialized at contract deployment, to establish a +clear timeframe for accepting contributions. The `withinDeadline` modifier will then enforce +this constraint, ensuring contributions are made within the allowed period. + +**Enhanced Contract:** + +```solidity +uint256 public deadline; + +function initialize(uint256 _fundingGoal, uint256 _duration) public initializer { + owner = msg.sender; + fundingGoal = _fundingGoal; + deadline = block.timestamp + _duration; +} + +modifier withinDeadline() { + require(block.timestamp <= deadline, "Funding period has ended"); + _; +} + +function contribute() public payable withinDeadline { + // Existing contribution logic +} +``` + +**Deadline Extension Capability:** + +To provide flexibility, a new function allows the owner to extend the deadline, +offering adaptability to changing campaign needs. + +```solidity +function extendDeadline(uint256 _newDuration) public { + require(msg.sender == owner, "Only the owner can extend the deadline"); + deadline = block.timestamp + _newDuration; +} +``` + +This upgrade not only introduces the element of time to the campaign but also +exemplifies the use of `modifiers` for enforcing contract conditions. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +::code-group + +```bash [yarn] +yarn compile +``` + +```bash [pnpm] +pnpm run compile +``` + +```bash [npm] +npm run compile +``` + +```bash [bun] +bun run compile +``` + +:: + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +Compiling contracts for zkSync Era with zksolc v1.4.0 and solc v0.8.17 +Compiling 2 Solidity file +Successfully compiled 2 Solidity file +``` + +The compiled artifacts will be located in the `/artifacts-zk` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/tests` directory, specifically the +`crowdFunding.test.ts` file. + +```typescript +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { getWallet, LOCAL_RICH_WALLETS, deployContract } from "../deploy/utils"; + +describe("CrowdfundingCampaign", function () { + let campaign; + let owner, addr1, addr2; + + beforeEach(async function () { + owner = getWallet(LOCAL_RICH_WALLETS[0].privateKey); + addr1 = getWallet(LOCAL_RICH_WALLETS[1].privateKey); + addr2 = getWallet(LOCAL_RICH_WALLETS[2].privateKey); + const fundingGoalInWei = ethers.parseEther('1').toString(); + campaign = await deployContract("CrowdfundingCampaign", [fundingGoalInWei], { wallet: owner, silent: true }); + }); + + describe("Contribute", function () { + it("should reject contributions of 0", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("0") })).to.be.revertedWith("Contribution must be greater than 0"); + }); + + it("should aggregate contributions in totalFundsRaised", async function () { + await campaign.connect(addr1).contribute({ value: ethers.parseEther("0.5") }); + await campaign.connect(addr2).contribute({ value: ethers.parseEther("0.3") }); + expect(await campaign.getTotalFundsRaised()).to.equal(ethers.parseEther("0.8")); + }); + + it("should emit GoalReached event when funding goal is met", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("1") })) + .to.emit(campaign, "GoalReached") + .withArgs(ethers.parseEther("1")); + }); + }); +}); +``` + +Key Components: + +**Testing Workflow:** + +- **Initialization**: Each test case initializes with fresh contract instances and predefined +rich wallet accounts to simulate various contributors and the contract owner. +- **Deployment**: The `CrowdfundingCampaign` contract is deployed using the `deployContract` +utility, setting a specific funding goal for each test scenario. + +**`contribute` Method Tests:** + +- **Zero Contributions**: Verifies that the contract correctly rejects contribution attempts with +zero value, ensuring the integrity of the contribution process. +- **Funds Aggregation**: Tests the contract's ability to accurately aggregate contributions from +multiple addresses and update the `totalFundsRaised` accordingly. +- **Goal Achievement**: Checks for the `GoalReached` event emission upon meeting the funding goal, +confirming the contract's responsiveness to achieving its set target. + +#### Execute tests +Execute the test command corresponding to your package manager: + +::code-group + +```bash [yarn] +yarn hardhat test --network hardhat +``` + +```bash [pnpm] +pnpm run hardhat test --network hardhat +``` + +```bash [npm] +npm run hardhat test --network hardhat +``` + +```bash [bun] +bun run hardhat test --network hardhat +``` + +:: + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash + CrowdfundingCampaign + Contribute + ✔ should reject contributions of 0 (45ms) + ✔ should aggregate contributions in totalFundsRaised (213ms) + ✔ should emit GoalReached event when funding goal is met (113ms) + + + 3 passing (1s) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. diff --git a/content/10.quick-start/_upgrading/_transparent_proxy_contract_upgradability.md b/content/10.quick-start/_upgrading/_transparent_proxy_contract_upgradability.md new file mode 100644 index 00000000..58c7b4ac --- /dev/null +++ b/content/10.quick-start/_upgrading/_transparent_proxy_contract_upgradability.md @@ -0,0 +1,35 @@ +--- +title: Transparent Proxy Contract Upgradeability +--- + +### What is a transparent upgradeable contract? +Transparent Upgradeable Contracts utilize the proxy pattern to facilitate post-deployment +logic updates while preventing accidental function collisions. They consist of: + +1. **Proxy Contract**: Manages storage, balance, and delegates calls to the logic contract, +except for those by the admin, ensuring clear separation between user and administrative interactions. +2. **Logic Contract**: Houses the actual business logic, upgradeable by swapping out for new versions. +3. **Admin Address**: Holds the rights to upgrade the logic contract, with its commands executed +exclusively by the proxy to prevent unintended logic execution. + +This setup ensures only non-administrative calls reach the logic contract, allowing +for safe and seamless upgrades. By switching the logic contract to a newer version +while keeping the original proxy intact, the contract's state and balance are preserved. +This facilitates improvements or bug fixes without changing the proxy, maintaining a +consistent user interface. + +## Framework selection + +Select the framework you want to get started using zkSync Era with. + +::content-switcher +--- +items: [{ + label: 'Hardhat', + partial: '_upgrading/_transparent/_hardhat_transparent_contract_upgradability' +}, { + label: 'Foundry', + partial: '_upgrading/_transparent/_foundry_transparent_contract_upgradability' +}] +--- +:: diff --git a/content/10.quick-start/_upgrading/_uups/_foundry_uups_contract_upgradability.md b/content/10.quick-start/_upgrading/_uups/_foundry_uups_contract_upgradability.md new file mode 100644 index 00000000..ae788953 --- /dev/null +++ b/content/10.quick-start/_upgrading/_uups/_foundry_uups_contract_upgradability.md @@ -0,0 +1,216 @@ +--- +title: Hardhat | Contract Testing +--- + +`foundry-zksync` is a specialized fork of Foundry, tailored for zkSync. +It extends Foundry's capabilities for Ethereum app development to support zkSync, +allowing for the compilation, deployment, testing, and interaction with smart contracts on zkSync. + +::callout{icon="i-heroicons-information-circle-16-solid" color="amber"} +`foundry-zksync` is still in an alpha stage, so some features might not be fully supported +yet and may not work as fully intended. It is open-sourced and contributions are welcomed. +:: + +## Step 1: Environment Configuration + +## Step 2: Test Wallet Configuration + +## Step 3: Testing `CrowdfundingCampaign` contract + +Now that our setup is complete, it's time to focus on the core of this +guide - testing our `CrowdfundingCampaign.sol` contract. Here's a quick +refresher on its structure: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract CrowdfundingCampaign { + address public owner; + uint256 public fundingGoal; + uint256 public totalFundsRaised; + mapping(address => uint256) public contributions; + + event ContributionReceived(address contributor, uint256 amount); + event GoalReached(uint256 totalFundsRaised); + + constructor(uint256 _fundingGoal) { + owner = msg.sender; + fundingGoal = _fundingGoal; + } + + function contribute() public payable { + require(msg.value > 0, "Contribution must be greater than 0"); + contributions[msg.sender] += msg.value; + totalFundsRaised += msg.value; + + emit ContributionReceived(msg.sender, msg.value); + + if (totalFundsRaised >= fundingGoal) { + emit GoalReached(totalFundsRaised); + } + } + + function withdrawFunds() public { + require(msg.sender == owner, "Only the owner can withdraw funds"); + require(totalFundsRaised >= fundingGoal, "Funding goal not reached"); + + uint256 amount = address(this).balance; + totalFundsRaised = 0; + + (bool success, ) = payable(owner).call{value: amount}(""); + require(success, "Transfer failed."); + } + + function getTotalFundsRaised() public view returns (uint256) { + return totalFundsRaised; + } + + function getFundingGoal() public view returns (uint256) { + return fundingGoal; + } +} +``` + +Thorough testing involves scrutinizing every function and aspect of our contract, +including potential failure scenarios. In this guide, we'll focus in on the `contribute` +method to ensure it's tested. + +As a challenge to hone your testing skills further, +consider devising additional tests for the `withdrawFunds`, `getTotalFundsRaised`, +and `getFundingGoal` methods, expanding your test coverage and reinforcing the +reliability of the contract. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +```bash +forge build --zksync +``` + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +[⠒] Compiling... +[⠃] Compiling 22 files with 0.8.20 +[⠊] Solc 0.8.20 finished in 736.48ms +Compiler run successful! +Compiling contracts for zkSync Era with zksolc v1.4.0 +``` + +The compiled zkEVM artifacts will be located in the `/zkout` folder, and the solc artifacts will be +located in the `/out` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/test` directory, specifically the +`CrowdfundingCampaign.t.sol` file. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../src/CrowdfundingCampaign.sol"; + +contract CrowdfundingCampaignTest is Test { + CrowdfundingCampaign campaign; + event GoalReached(uint256 totalFundsRaised); + address owner; + address addr1; + address addr2; + + function setUp() public { + owner = address(this); + + addr1 = vm.addr(1); + addr2 = vm.addr(2); + + campaign = new CrowdfundingCampaign(1 ether); + console.log("CrowdfundingCampaign deployed at: %s", address(campaign)); + } + + function test_RejectZeroContributions() public { + vm.expectRevert("Contribution must be greater than 0"); + campaign.contribute{value: 0}(); + } + + function test_AggregateContributions() public { + uint256 initialTotal = campaign.getTotalFundsRaised(); + + vm.prank(addr1); + vm.deal(addr1, 2 ether); + campaign.contribute{value: 0.5 ether}(); + + vm.prank(addr2); + vm.deal(addr2, 2 ether); + campaign.contribute{value: 0.3 ether}(); + + assertEq(campaign.getTotalFundsRaised(), initialTotal + 0.8 ether); + } + + function test_EmitGoalReachedWhenFundingGoalMet() public { + vm.prank(addr1); + vm.deal(addr1, 2 ether); + vm.expectEmit(true, true, false, true); + emit GoalReached(1 ether); + campaign.contribute{value: 1 ether}(); + } +} + +``` + +**Testing Workflow:** + +- **Environment Setup**: Leverages Foundry's `Test` contract and setup functions +to prepare the test environment, ensuring a fresh state for each test case. +- **Deployment and Address Simulation**: Deploys the `CrowdfundingCampaign` contract +within the test setup and simulates addresses using Foundry's `vm.addr()` function for +various test actors. + +**`contribute` Method Tests:** + +- **Zero Contribution Validation**: Asserts that the contract rejects contribution +attempts with zero value, testing the contract's input validation logic. +- **Contribution Aggregation**: Confirms the contract's ability to correctly tally +contributions from various addresses, ensuring accurate tracking of the total funds raised. +- **Event Emission Upon Goal Achievement**: Utilizes Foundry's `vm.expectEmit` to +anticipate the `GoalReached` event when the funding goal is met, validating the +contract's event logic and state transitions. + +#### Execute tests + +Execute the test command: + +```bash +forge test --zksync +``` + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash +Ran 3 tests for test/CrowdfundingCampaign.t.sol:CrowdfundingCampaignTest +[PASS] test_AggregateContributions() (gas: 29204) +[PASS] test_EmitGoalReachedWhenFundingGoalMet() (gas: 18862) +[PASS] test_RejectZeroContributions() (gas: 8148) +Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 44.03ms (43.94ms CPU time) + +Ran 1 test suite in 48.11ms (44.03ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. diff --git a/content/10.quick-start/_upgrading/_uups/_hardhat_uups_contract_upgradability.md b/content/10.quick-start/_upgrading/_uups/_hardhat_uups_contract_upgradability.md new file mode 100644 index 00000000..f32448fe --- /dev/null +++ b/content/10.quick-start/_upgrading/_uups/_hardhat_uups_contract_upgradability.md @@ -0,0 +1,272 @@ +--- +title: Hardhat | Contract Upgrading +--- + +Hardhat is an Ethereum development environment, designed for easy smart contract development in Solidity. +zkSync provides its own plugins which makes working with contracts on zkSync simple and efficient. + +## Step 1: Environment Configuration + +## Step 2: Test Wallet Configuration + +## Step 3: Adapting `CrowdfundingCampaign.sol` contract for upgradeability + +To adapt our `CrowdfundingCampaign.sol` contract for upgradeability, we're +transitioning to a Transparent Proxy pattern. This approach separates the +contract's logic (which can be upgraded) from its persistent state +(stored in the proxy). + +**Refactoring for Proxy Compatibility:** + +We're refactoring the contract to initialize state variables through an +`initialize` function instead of the constructor, in line with the +Transparent Proxy pattern. + +**Updated Contract Structure:** + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +contract CrowdfundingCampaign is Initializable { + address public owner; + uint256 public fundingGoal; + uint256 public deadline; + + event ContributionReceived(address contributor, uint256 amount); + + function initialize(uint256 _fundingGoal, uint256 _duration) public initializer { + owner = msg.sender; + fundingGoal = _fundingGoal; + deadline = block.timestamp + _duration; + } + + function contribute() public payable { + // contribution logic remains the same + } + + function withdrawFunds() public { + // withdrawFunds logic remains the same + } + + function getTotalFundsRaised() public view returns (uint256) { + // getTotalFundsRaised remains the same + } + + function getFundingGoal() public view returns (uint256) { + // getFundingGoal remains the same + } +} +``` + +**Key Modifications:** + +- **Initializable**: Inherits from OpenZeppelin's `Initializable` to ensure the `initialize` function +can only be called once, similar to a constructor. +- **Initialize Function**: Replaces the constructor for setting initial state, facilitating upgrades +through new logic contracts. +- **Transparent Proxy Pattern**: Utilizes a proxy contract to delegate calls to this logic contract, +allowing for future upgrades without losing the contract's state. + +This restructuring not only prepares the `CrowdfundingCampaign` contract for upgradeability. + +## Step 4: Upgrading the `CrowdfundingCampaign` Contract + +With our initial setup in place, we're ready to enhance our `CrowdfundingCampaign.sol` +contract by incorporating a deadline for contributions. This addition not only brings +a new layer of functionality but also introduces the concept of time-based conditions +through a `modifier`. + +**Current Contract Overview:** + +The existing version of our contract allows for open-ended contributions towards a +funding goal, without any time constraints. + +**Proposed Upgrade:** + +We're introducing a `deadline` variable, initialized at contract deployment, to establish a +clear timeframe for accepting contributions. The `withinDeadline` modifier will then enforce +this constraint, ensuring contributions are made within the allowed period. + +**Enhanced Contract:** + +```solidity +uint256 public deadline; + +function initialize(uint256 _fundingGoal, uint256 _duration) public initializer { + owner = msg.sender; + fundingGoal = _fundingGoal; + deadline = block.timestamp + _duration; +} + +modifier withinDeadline() { + require(block.timestamp <= deadline, "Funding period has ended"); + _; +} + +function contribute() public payable withinDeadline { + // Existing contribution logic +} +``` + +**Deadline Extension Capability:** + +To provide flexibility, a new function allows the owner to extend the deadline, +offering adaptability to changing campaign needs. + +```solidity +function extendDeadline(uint256 _newDuration) public { + require(msg.sender == owner, "Only the owner can extend the deadline"); + deadline = block.timestamp + _newDuration; +} +``` + +This upgrade not only introduces the element of time to the campaign but also +exemplifies the use of `modifiers` for enforcing contract conditions. + +### Compile contract + +Smart contracts deployed to zkSync must be compiled using our custom compiler. +For this particular guide we are making use of `zksolc`. + +To compile the contracts in the project, run the following command: + +::code-group + +```bash [yarn] +yarn compile +``` + +```bash [pnpm] +pnpm run compile +``` + +```bash [npm] +npm run compile +``` + +```bash [bun] +bun run compile +``` + +:: + +#### Expected Output + +Upon successful compilation, you'll receive output detailing the +`zksolc` and `solc` versions used during compiling and the number +of Solidity files compiled. + +```bash +Compiling contracts for zkSync Era with zksolc v1.4.0 and solc v0.8.17 +Compiling 2 Solidity file +Successfully compiled 2 Solidity file +``` + +The compiled artifacts will be located in the `/artifacts-zk` folder. + +### Testing + +This section describes the testing `CrowdfundingCampaign.sol` contract. Let's +start by reviewing the tests for `CrowdfundingCampaign.sol` contract provided +during the initialization step in the `/tests` directory, specifically the +`crowdFunding.test.ts` file. + +```typescript +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { getWallet, LOCAL_RICH_WALLETS, deployContract } from "../deploy/utils"; + +describe("CrowdfundingCampaign", function () { + let campaign; + let owner, addr1, addr2; + + beforeEach(async function () { + owner = getWallet(LOCAL_RICH_WALLETS[0].privateKey); + addr1 = getWallet(LOCAL_RICH_WALLETS[1].privateKey); + addr2 = getWallet(LOCAL_RICH_WALLETS[2].privateKey); + const fundingGoalInWei = ethers.parseEther('1').toString(); + campaign = await deployContract("CrowdfundingCampaign", [fundingGoalInWei], { wallet: owner, silent: true }); + }); + + describe("Contribute", function () { + it("should reject contributions of 0", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("0") })).to.be.revertedWith("Contribution must be greater than 0"); + }); + + it("should aggregate contributions in totalFundsRaised", async function () { + await campaign.connect(addr1).contribute({ value: ethers.parseEther("0.5") }); + await campaign.connect(addr2).contribute({ value: ethers.parseEther("0.3") }); + expect(await campaign.getTotalFundsRaised()).to.equal(ethers.parseEther("0.8")); + }); + + it("should emit GoalReached event when funding goal is met", async function () { + await expect(campaign.connect(addr1).contribute({ value: ethers.parseEther("1") })) + .to.emit(campaign, "GoalReached") + .withArgs(ethers.parseEther("1")); + }); + }); +}); +``` + +Key Components: + +**Testing Workflow:** + +- **Initialization**: Each test case initializes with fresh contract instances and predefined +rich wallet accounts to simulate various contributors and the contract owner. +- **Deployment**: The `CrowdfundingCampaign` contract is deployed using the `deployContract` +utility, setting a specific funding goal for each test scenario. + +**`contribute` Method Tests:** + +- **Zero Contributions**: Verifies that the contract correctly rejects contribution attempts with +zero value, ensuring the integrity of the contribution process. +- **Funds Aggregation**: Tests the contract's ability to accurately aggregate contributions from +multiple addresses and update the `totalFundsRaised` accordingly. +- **Goal Achievement**: Checks for the `GoalReached` event emission upon meeting the funding goal, +confirming the contract's responsiveness to achieving its set target. + +#### Execute tests +Execute the test command corresponding to your package manager: + +::code-group + +```bash [yarn] +yarn hardhat test --network hardhat +``` + +```bash [pnpm] +pnpm run hardhat test --network hardhat +``` + +```bash [npm] +npm run hardhat test --network hardhat +``` + +```bash [bun] +bun run hardhat test --network hardhat +``` + +:: + +#### Expected Output + +Upon completion, the test suite will provide a summary of all executed tests, +indicating their success or failure: + +```bash + CrowdfundingCampaign + Contribute + ✔ should reject contributions of 0 (45ms) + ✔ should aggregate contributions in totalFundsRaised (213ms) + ✔ should emit GoalReached event when funding goal is met (113ms) + + + 3 passing (1s) +``` + +🎉 Congratulations! The `contribute` method of the `CrowdfundingCampaign` contract +has been thoroughly tested and is ready for action. diff --git a/content/10.quick-start/_upgrading/_uups_contract_upgradability.md b/content/10.quick-start/_upgrading/_uups_contract_upgradability.md new file mode 100644 index 00000000..d8a31a68 --- /dev/null +++ b/content/10.quick-start/_upgrading/_uups_contract_upgradability.md @@ -0,0 +1,33 @@ +--- +title: UUPS Proxy Contract Upgradeability +--- + +### What is a UUPS upgradeable contract? +UUPS (Universal Upgradeable Proxy Standard) Upgradeable Contracts embed the upgrade logic +within the contract itself, simplifying upgrades and enhancing security. The components are: + +1. **Proxy Contract**: Contains minimal logic, primarily delegating calls to the implementation +contract. Unlike other proxies, it doesn't require a separate upgrade function. +2. **Implementation Contract**: Houses the business logic and the upgrade functionality, +enabling the contract to upgrade itself from within. +3. **Admin Role**: Assigned to an entity with the authority to initiate upgrades, ensuring +controlled access to the upgrade function. + +In UUPS contracts, upgrades are performed by invoking the upgrade function within the +implementation contract, which updates the proxy's reference to point to a new implementation. +This self-contained approach minimizes the proxy's complexity and gas costs, while the +implementation contract's built-in upgrade mechanism ensures only authorized upgrades. +The contract's state remains intact across upgrades, facilitating continuous improvement +with a stable user experience. + +::content-switcher +--- +items: [{ + label: 'Hardhat', + partial: '_upgrading/_uups/_hardhat_transparent_contract_upgradability' +}, { + label: 'Foundry', + partial: '_upgrading/_uups/_foundry_transparent_contract_upgradability' +}] +--- +:: diff --git a/cspell-config/cspell-blockchain.txt b/cspell-config/cspell-blockchain.txt index c855ab20..522b9c6e 100644 --- a/cspell-config/cspell-blockchain.txt +++ b/cspell-config/cspell-blockchain.txt @@ -1,3 +1,7 @@ evm merkle keccak +upgradability +Initializable +initializable +UUPS \ No newline at end of file diff --git a/cspell-config/cspell-dev.txt b/cspell-config/cspell-dev.txt index c29eb22f..c6264bc5 100644 --- a/cspell-config/cspell-dev.txt +++ b/cspell-config/cspell-dev.txt @@ -10,3 +10,4 @@ zksolc zkvyper ZKEVM zkout +nomicfoundation \ No newline at end of file From 583f2d71f74816386261dc92828346714f141ed7 Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Tue, 9 Apr 2024 09:42:11 -0500 Subject: [PATCH 11/54] debug: adds dev tools for improved debugging --- bun.lockb | Bin 641937 -> 648051 bytes components/content/ContentSwitcher.vue | 5 ++++- nuxt.config.ts | 10 +++++++++- package.json | 1 + 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index 74387b20e8d808f078319e1444c057a9f13da4ec..a3281a85f8fe98373261213aca07c6e02f4a1ddd 100755 GIT binary patch delta 50390 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 V2vds!$~zKbMx>2Zc#Yru{{vfTFSq~z delta 47584 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�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 v(js}x_>B5P(); - const selectedIndex = ref(0); function onTabChange(index: number) { @@ -23,6 +22,10 @@ const route = useRoute(); v-show="selectedIndex === index" :key="item.partial" > +
+ {{ console.log('Route path:', $route.path) }} + {{ console.log('Rendering item:', item.partial) }} +
{ diff --git a/package.json b/package.json index 900dc939..69adcb6c 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "author": "Matter Labs", "homepage": "https://zksync.io", "devDependencies": { + "@nuxt/devtools": "^1.1.5", "@nuxt/eslint-config": "^0.2.0", "@nuxthq/studio": "^1.0.13", "@types/bun": "^1.0.8", From 4ebae9ff12920e6cb51ad69f9d3087c3d72adbab Mon Sep 17 00:00:00 2001 From: Dustin Brickwood Date: Tue, 9 Apr 2024 11:59:07 -0500 Subject: [PATCH 12/54] fix: fix content switch basepath issue --- components/content/ContentSwitcher.vue | 9 ++++----- .../_beacon/_foundry_beacon_contract_upgradability.md | 2 +- .../_beacon/_hardhat_beacon_contract_upgradability.md | 2 +- .../_upgrading/_beacon_proxy_contract_upgradability.md | 4 ++-- .../_uups/_foundry_uups_contract_upgradability.md | 2 +- .../_upgrading/_uups_contract_upgradability.md | 4 ++-- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/components/content/ContentSwitcher.vue b/components/content/ContentSwitcher.vue index dcf55e18..6e23d6c3 100644 --- a/components/content/ContentSwitcher.vue +++ b/components/content/ContentSwitcher.vue @@ -8,6 +8,9 @@ function onTabChange(index: number) { selectedIndex.value = index; } const route = useRoute(); +// This splits the path into segments and takes the first one +// TODO: This is a temporary solution, we need to find a better way to get the base path +const basePath = route.path.split('/')[1];