diff --git a/templates/quickstart/foundry/testing/.github/workflows/test.yml b/templates/quickstart/foundry/testing/.github/workflows/test.yml deleted file mode 100644 index 9282e82..0000000 --- a/templates/quickstart/foundry/testing/.github/workflows/test.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: test - -on: workflow_dispatch - -env: - FOUNDRY_PROFILE: ci - -jobs: - check: - strategy: - fail-fast: true - - name: Foundry project - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Run Forge build - run: | - forge --version - forge build --sizes - id: build - - - name: Run Forge tests - run: | - forge test -vvv - id: test diff --git a/templates/quickstart/foundry/testing/.gitignore b/templates/quickstart/foundry/testing/.gitignore index 85198aa..b1d1fc2 100644 --- a/templates/quickstart/foundry/testing/.gitignore +++ b/templates/quickstart/foundry/testing/.gitignore @@ -1,6 +1,7 @@ # Compiler files cache/ out/ +zkout/ # Ignores development broadcast logs !/broadcast @@ -12,3 +13,4 @@ docs/ # Dotenv file .env +broadcast/ \ No newline at end of file diff --git a/templates/quickstart/foundry/testing/foundry.toml b/templates/quickstart/foundry/testing/foundry.toml index 25b918f..b964eed 100644 --- a/templates/quickstart/foundry/testing/foundry.toml +++ b/templates/quickstart/foundry/testing/foundry.toml @@ -3,4 +3,8 @@ src = "src" out = "out" libs = ["lib"] +[rpc_endpoints] +zkSyncSepoliaTestnet = "https://sepolia.era.zksync.dev" +inMemoryNode = "http://127.0.0.1:8011" + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/templates/quickstart/foundry/testing/script/Counter.s.sol b/templates/quickstart/foundry/testing/script/Counter.s.sol deleted file mode 100644 index df9ee8b..0000000 --- a/templates/quickstart/foundry/testing/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/templates/quickstart/foundry/testing/script/DeployFactory.s.sol b/templates/quickstart/foundry/testing/script/DeployFactory.s.sol new file mode 100644 index 0000000..971fea0 --- /dev/null +++ b/templates/quickstart/foundry/testing/script/DeployFactory.s.sol @@ -0,0 +1,31 @@ +// 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(); + } +} diff --git a/templates/quickstart/foundry/testing/src/Counter.sol b/templates/quickstart/foundry/testing/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/templates/quickstart/foundry/testing/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/templates/quickstart/foundry/testing/src/CrowdfundFactory.sol b/templates/quickstart/foundry/testing/src/CrowdfundFactory.sol new file mode 100644 index 0000000..6ef4fcc --- /dev/null +++ b/templates/quickstart/foundry/testing/src/CrowdfundFactory.sol @@ -0,0 +1,23 @@ +// 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; + } +} diff --git a/templates/quickstart/foundry/testing/src/CrowdfundingCampaign.sol b/templates/quickstart/foundry/testing/src/CrowdfundingCampaign.sol new file mode 100644 index 0000000..11b33a0 --- /dev/null +++ b/templates/quickstart/foundry/testing/src/CrowdfundingCampaign.sol @@ -0,0 +1,48 @@ +// 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; + } +} diff --git a/templates/quickstart/foundry/testing/test/Counter.t.sol b/templates/quickstart/foundry/testing/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/templates/quickstart/foundry/testing/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/templates/quickstart/foundry/testing/test/CrowdfundingCampaign.t.sol b/templates/quickstart/foundry/testing/test/CrowdfundingCampaign.t.sol new file mode 100644 index 0000000..5d70437 --- /dev/null +++ b/templates/quickstart/foundry/testing/test/CrowdfundingCampaign.t.sol @@ -0,0 +1,51 @@ +// 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}(); + } +}