Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add zksync-101 template #22

Merged
merged 1 commit into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions templates/101/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# DO NOT PUT REAL PRIVATE KEYS INTO THIS FILE
# This is a local rich wallet private key for development and learning purposes only
WALLET_PRIVATE_KEY=0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110
33 changes: 33 additions & 0 deletions templates/101/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# dotenv environment variable files

.env
.env.development.local
.env.test.local
.env.production.local
.env.local
.env.*
!.env.example

# Finder (MacOS) folder config
.DS_Store

node_modules/

era_test_node.log

package-lock.json
node_modules
typechain-types
typechain

**/.upgradable/
build-test/
dist
artifacts-zk/
cache-zk/
deployments-zk/
deployments/

# Hardhat files
cache
artifacts
1 change: 1 addition & 0 deletions templates/101/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
legacy-peer-deps=true
12 changes: 12 additions & 0 deletions templates/101/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"recommendations": [
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
"dbaeumer.vscode-eslint",
"editorconfig.editor",
"esbenp.prettier-vscode",
"mikestead.dotenv",
"yoavbls.pretty-ts-errors"
],
"unwantedRecommendations": []
}
11 changes: 11 additions & 0 deletions templates/101/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.trimTrailingWhitespace": true,
"editor.formatOnType": false,
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"editor.tabSize": 2,
"editor.insertSpaces": true,
"files.eol": "\n",
}
21 changes: 21 additions & 0 deletions templates/101/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Matter Labs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
66 changes: 66 additions & 0 deletions templates/101/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ZKsync 101

This project was scaffolded with [ZKsync CLI](https://github.com/matter-labs/zksync-cli).

ZKsync 101 is a quick start template to help you learn how to
be a super developer using ZKsync CLI to build projects in the
ZKsync ecosystem!

This project is a Nodejs project that uses hardhat, viem, and solidity.
You will learn how to build, deploy and test smart contracts with hardhat
onto the ZKsync in memory node and use ZKsync CLI to interact with the contracts.

## Project Layout

- `/contracts`: Contains solidity smart contracts.
- `/deploy`: Scripts for contract deployment and interaction.
- `/test`: Test files.
- `hardhat.config.ts`: Configuration settings, the default network is set to "inMemoryNode".

## How to Use

1. Install dependencies with `npm install --force`.

2. Configure your ZKsync CLI to use the In memory node settings for dev.

```bash
zksync-cli dev config
```

3. Start up a local in-memory node with ZKsync CLI with the following command:

```bash
zksync-cli dev start
```

4. Follow along on ZKsync Docs in our [ZKsync 101](https://docs.zksync.io/build/start-coding/zksync-101)!

### Environment Settings

This project pulls in environment variables from `.env` files.

Rename `.env.example` to `.env`, the provided private key is a local rich wallet
that is available in the local in-memory node.

```txt
WALLET_PRIVATE_KEY=your_private_key_here...
```

### Local Tests

Running `npm run test` by default runs the [ZKsync In-memory Node](https://docs.zksync.io/build/test-and-debug/in-memory-node) provided by the [@matterlabs/hardhat-zksync-node](https://docs.zksync.io/build/tooling/hardhat/hardhat-zksync-node) tool.

Important: ZKsync In-memory Node currently supports only the L2 node. If contracts also need L1, use another testing environment like Dockerized Node.
Refer to [test documentation](https://docs.zksync.io/build/test-and-debug) for details.

## Useful Links

- [Docs](https://docs.zksync.io)
- [Official Site](https://zksync.io/)
- [GitHub](https://github.com/matter-labs)
- [Twitter](https://twitter.com/zksync)
- [Discord](https://join.zksync.dev/)

## License

This project is under the [MIT](./LICENSE) license.
48 changes: 48 additions & 0 deletions templates/101/contracts/1-hello-zksync/CrowdfundingCampaign.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract CrowdfundingCampaign {
address public owner;
uint256 private fundingGoal;
uint256 private totalFundsRaised;
mapping(address => uint256) private 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Crowdfunding campaign contract
import "../1-hello-zksync/CrowdfundingCampaign.sol";

// Factory contract to create and manage crowdfunding campaigns
contract CrowdfundingFactory {
CrowdfundingCampaign[] private 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

// The BeaconCrowdfundingCampaign contract implements
// the Initializable interface from OpenZeppelin
contract BeaconCrowdfundingCampaign is Initializable {
address public owner;
uint256 private fundingGoal;
uint256 private totalFundsRaised;
mapping(address => uint256) private contributions;

event ContributionReceived(address contributor, uint256 amount);
event GoalReached(uint256 totalFundsRaised);

// The `initialize` function replaces the constructor in upgradeable contracts
function initialize(uint256 _fundingGoal) public initializer {
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

// The BeaconCrowdfundingCampaign contract implements
// the Initializable interface from OpenZeppelin
contract V2_BeaconCrowdfundingCampaign is Initializable {
address public owner;
uint256 private fundingGoal;
uint256 private totalFundsRaised;
mapping(address => uint256) private contributions;

// In V2 we add a deadline for the campaign
uint256 public deadline;
// We also add a flag to check if the V2 contract has been initialized
bool private initializedV2;

event ContributionReceived(address contributor, uint256 amount);
event GoalReached(uint256 totalFundsRaised);

// Original initialization function for V1
function initialize(uint256 _fundingGoal) public initializer {
owner = msg.sender;
fundingGoal = _fundingGoal;
}

// Additional initialization function for V2
function initializeV2(uint256 _duration) public {
require(!initializedV2, "V2 already initialized");
require(msg.sender == owner, "Only the owner can initialize V2");

deadline = block.timestamp + _duration;
initializedV2 = true;
}

// Modifier to check if the campaign is still within the deadline
// A modifier is a function that is executed
// before the function that uses it
modifier withinDeadline() {
require(block.timestamp <= deadline, "Funding period has ended");
_;
}

// We use the `withinDeadline` modifier to ensure
// that contributions are only accepted within the deadlin
function contribute() public payable withinDeadline {
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);
}
}

// We add a new function to extend the deadline
// only by the owner of the campaign
function extendDeadline(uint256 _newDuration) public {
require(msg.sender == owner, "Only the owner can extend the deadline");
deadline = block.timestamp + _newDuration;
}

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;
}
}
Loading
Loading