From e46ce5aa1cd2b94a6dfdd1f492ac855b6c36a6dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?rich=CE=9Brd?= Date: Fri, 1 Nov 2024 10:23:28 -0400 Subject: [PATCH] feat: deploy to Linea (#18) --- .gitmodules | 3 ++ README.md | 27 +++++++++++---- foundry.toml | 1 + lib/foundry-devops | 1 + package.json | 10 ++++-- script/Deploy.s.sol | 78 ++++++++++++++++++++++++++++++++++++++------ src/Membership.sol | 1 - test/WakuRlnV2.t.sol | 20 +++++++----- 8 files changed, 112 insertions(+), 29 deletions(-) create mode 160000 lib/foundry-devops diff --git a/.gitmodules b/.gitmodules index 81f929e..b9916b8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,3 +9,6 @@ [submodule "lib/openzeppelin-contracts-upgradeable"] path = lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "lib/foundry-devops"] + path = lib/foundry-devops + url = https://github.com/Cyfrin/foundry-devops diff --git a/README.md b/README.md index 855f3fa..d485c88 100644 --- a/README.md +++ b/README.md @@ -139,17 +139,30 @@ $ forge test ## Owner privileges -The contract implementation aims to follow the [specification](https://github.com/waku-org/specs/blob/81b9fd588bff39894608746774b0903b067b5cdf/standards/core/rln-contract.md) that also describes ownership (see [Governance and upgradability](https://github.com/waku-org/specs/blob/81b9fd588bff39894608746774b0903b067b5cdf/standards/core/rln-contract.md#governance-and-upgradability) section). +The contract implementation aims to follow the +[specification](https://github.com/waku-org/specs/blob/81b9fd588bff39894608746774b0903b067b5cdf/standards/core/rln-contract.md) +that also describes ownership (see +[Governance and upgradability](https://github.com/waku-org/specs/blob/81b9fd588bff39894608746774b0903b067b5cdf/standards/core/rln-contract.md#governance-and-upgradability) +section). As of commit afb858, the `Owner` privileges are assigned to the `msg.sender` of the membership registration transaction. The `Owner` has the following privileges: -- set the token and price of one message published per epoch ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/LinearPriceCalculator.sol#L25)); -- authorize upgrades to a new implementation contract ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L99)); -- set the price calculator contract address ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L267)); -- set the maximum total rate limit of all memberships in the membership set ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L273)); -- set the minimum ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L287)) and maximum ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L280)) rate limit of one membership; -- set the duration of the active period ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L295)) and grace period ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L302)) of new memberships (see the [state transition diagram of a membership](https://github.com/waku-org/specs/blob/81b9fd588bff39894608746774b0903b067b5cdf/standards/core/rln-contract.md#membership-lifecycle)). +- set the token and price of one message published per epoch + ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/LinearPriceCalculator.sol#L25)); +- authorize upgrades to a new implementation contract + ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L99)); +- set the price calculator contract address + ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L267)); +- set the maximum total rate limit of all memberships in the membership set + ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L273)); +- set the minimum ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L287)) and maximum + ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L280)) rate limit of one + membership; +- set the duration of the active period + ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L295)) and grace period + ([link](https://github.com/waku-org/waku-rlnv2-contract/blob/main/src/WakuRlnV2.sol#L302)) of new memberships (see the + [state transition diagram of a membership](https://github.com/waku-org/specs/blob/81b9fd588bff39894608746774b0903b067b5cdf/standards/core/rln-contract.md#membership-lifecycle)). The pause functionality for contract functions is not yet implemented. diff --git a/foundry.toml b/foundry.toml index 7183896..5fa3809 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,6 +16,7 @@ solc = "0.8.24" src = "src" test = "test" + fs_permissions = [{ access = "read", path = "./broadcast" }] [fuzz] max_test_rejects = 128_000 diff --git a/lib/foundry-devops b/lib/foundry-devops new file mode 160000 index 0000000..47393d0 --- /dev/null +++ b/lib/foundry-devops @@ -0,0 +1 @@ +Subproject commit 47393d0a85ad9f6aa127ba2aed2bf9a7a7488bcf diff --git a/package.json b/package.json index f338a6d..96b2dd7 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,13 @@ "adorno": "pnpm prettier:write && forge fmt && forge snapshot && pnpm gas-report", "deploy:sepolia": "./envCheck.sh && FOUNDRY_PROFILE=sepolia forge script --chain sepolia script/Deploy.s.sol:Deploy --rpc-url $RPC_URL --broadcast --verify -vv --account $ACCOUNT --legacy --sender $ETH_FROM", "deploy:cardona": "export RPC_URL=https://rpc.cardona.zkevm-rpc.com && ./envCheck.sh && FOUNDRY_PROFILE=cardona forge script --chain 2442 script/Deploy.s.sol:Deploy --rpc-url $RPC_URL --broadcast --verify -vv --account $ACCOUNT --legacy --sender $ETH_FROM", - "deploy:linea_sepolia": "export RPC_URL=https://rpc.sepolia.linea.build && ./envCheck.sh && FOUNDRY_PROFILE=linea_sepolia forge script --chain 59141 script/Deploy.s.sol:Deploy --rpc-url $RPC_URL --broadcast --verify -vv --account $ACCOUNT --legacy --sender $ETH_FROM", - "deploy:localhost": "./envCheck.sh && forge script script/Deploy.s.sol:Deploy --rpc-url $RPC_URL --broadcast -vv --account $ACCOUNT --sender $ETH_FROM" + "deploy:localhost:price_calculator": "./envCheck.sh && forge script script/Deploy.s.sol:DeployPriceCalculator --rpc-url $RPC_URL --broadcast -vv --sender $ETH_FROM --account $ACCOUNT", + "deploy:localhost:wakurln_impl_v2": "./envCheck.sh && forge script script/Deploy.s.sol:DeployWakuRlnV2 --rpc-url $RPC_URL --broadcast -vv --sender $ETH_FROM --account $ACCOUNT", + "deploy:localhost:proxy": "./envCheck.sh && forge script script/Deploy.s.sol:DeployProxy --rpc-url $RPC_URL --broadcast -vv --sender $ETH_FROM --account $ACCOUNT", + "deploy:localhost": "npm run deploy:localhost:price_calculator && npm run deploy:localhost:wakurln_impl_v2 && npm run deploy:localhost:proxy", + "deploy:linea_sepolia:price_calculator": "export RPC_URL=https://rpc.sepolia.linea.build && ./envCheck.sh && FOUNDRY_PROFILE=linea_sepolia forge script --chain 59141 script/Deploy.s.sol:DeployPriceCalculator --rpc-url $RPC_URL --broadcast --verify -vv --account $ACCOUNT --legacy --sender $ETH_FROM", + "deploy:linea_sepolia:wakurln_impl_v2": "export RPC_URL=https://rpc.sepolia.linea.build && ./envCheck.sh && FOUNDRY_PROFILE=linea_sepolia forge script --chain 59141 script/Deploy.s.sol:DeployWakuRlnV2 --rpc-url $RPC_URL --broadcast --verify -vv --account $ACCOUNT --legacy --sender $ETH_FROM", + "deploy:linea_sepolia:proxy": "export RPC_URL=https://rpc.sepolia.linea.build && ./envCheck.sh && FOUNDRY_PROFILE=linea_sepolia forge script --chain 59141 script/Deploy.s.sol:DeployProxy --rpc-url $RPC_URL --broadcast --verify -vv --account $ACCOUNT --legacy --sender $ETH_FROM", + "deploy:linea_sepolia": "npm run deploy:linea_sepolia:price_calculator && npm run deploy:linea_sepolia:wakurln_impl_v2 && npm run deploy:linea_sepolia:proxy" } } diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index cc8b1ba..7b926ef 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -2,25 +2,24 @@ pragma solidity >=0.8.19 <=0.9.0; import { WakuRlnV2 } from "../src/WakuRlnV2.sol"; +import { IPriceCalculator } from "../src/IPriceCalculator.sol"; import { LinearPriceCalculator } from "../src/LinearPriceCalculator.sol"; import { PoseidonT3 } from "poseidon-solidity/PoseidonT3.sol"; import { LazyIMT } from "@zk-kit/imt.sol/LazyIMT.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - +import { DevOpsTools } from "lib/foundry-devops/src/DevOpsTools.sol"; import { BaseScript } from "./Base.s.sol"; +import "forge-std/console.sol"; -contract Deploy is BaseScript { - function run() public broadcast returns (WakuRlnV2 w, address impl) { +contract DeployPriceCalculator is BaseScript { + function run() public broadcast returns (address) { address _token = _getTokenAddress(); - return deploy(_token); + return address(deploy(_token)); } - function deploy(address _token) public returns (WakuRlnV2 w, address impl) { - address priceCalcAddr = address(new LinearPriceCalculator(_token, 0.05 ether)); - impl = address(new WakuRlnV2()); - bytes memory data = abi.encodeCall(WakuRlnV2.initialize, (priceCalcAddr, 160_000, 20, 600, 180 days, 30 days)); - address proxy = address(new ERC1967Proxy(impl, data)); - w = WakuRlnV2(proxy); + function deploy(address _token) public returns (IPriceCalculator) { + LinearPriceCalculator priceCalculator = new LinearPriceCalculator(_token, 0.05 ether); + return IPriceCalculator(priceCalculator); } function _getTokenAddress() internal view returns (address) { @@ -36,6 +35,65 @@ contract Deploy is BaseScript { } } +contract DeployWakuRlnV2 is BaseScript { + function run() public broadcast returns (address) { + return address(deploy()); + } + + function deploy() public returns (WakuRlnV2) { + return new WakuRlnV2(); + } +} + +contract DeployProxy is BaseScript { + uint32 public constant MAX_TOTAL_RATELIMIT_PER_EPOCH = 160_000; + uint32 public constant MIN_RATELIMIT_PER_MEMBERSHIP = 20; + uint32 public constant MAX_RATELIMIT_PER_MEMBERSHIP = 600; + uint32 public constant ACTIVE_DURATION = 180 days; + uint32 public constant GRACE_PERIOD_DURATION = 30 days; + + function run() public broadcast returns (address) { + address priceCalcAddr; + address wakuRlnV2ImplAddr; + + try vm.envAddress("PRICE_CALCULATOR_ADDRESS") returns (address envPriceCalcAddress) { + console.log("Loading price calculator address from environment variable"); + priceCalcAddr = envPriceCalcAddress; + } catch { + console.log("Loading price calculator address from broadcast directory"); + priceCalcAddr = DevOpsTools.get_most_recent_deployment("LinearPriceCalculator", block.chainid); + } + + try vm.envAddress("WAKURLNV2_ADDRESS") returns (address envWakuRlnV2ImplAddr) { + console.log("Loading WakuRlnV2 address from environment variable"); + wakuRlnV2ImplAddr = envWakuRlnV2ImplAddr; + } catch { + console.log("Loading WakuRlnV2 address from broadcast directory"); + wakuRlnV2ImplAddr = DevOpsTools.get_most_recent_deployment("WakuRlnV2", block.chainid); + } + + console.log("Using price calculator address: %s", priceCalcAddr); + console.log("Using WakuRLNV2 address: %s", wakuRlnV2ImplAddr); + + return address(deploy(priceCalcAddr, wakuRlnV2ImplAddr)); + } + + function deploy(address _priceCalcAddr, address _wakuRlnV2ImplAddr) public returns (ERC1967Proxy) { + bytes memory data = abi.encodeCall( + WakuRlnV2.initialize, + ( + _priceCalcAddr, + MAX_TOTAL_RATELIMIT_PER_EPOCH, + MIN_RATELIMIT_PER_MEMBERSHIP, + MAX_RATELIMIT_PER_MEMBERSHIP, + ACTIVE_DURATION, + GRACE_PERIOD_DURATION + ) + ); + return new ERC1967Proxy(_wakuRlnV2ImplAddr, data); + } +} + contract DeployLibs is BaseScript { function run() public broadcast returns (address poseidonT3, address lazyImt) { bytes memory poseidonT3Bytecode = type(PoseidonT3).creationCode; diff --git a/src/Membership.sol b/src/Membership.sol index ac08bb4..222549e 100644 --- a/src/Membership.sol +++ b/src/Membership.sol @@ -150,7 +150,6 @@ abstract contract MembershipUpgradeable is Initializable { internal onlyInitializing { - require(0 < _minMembershipRateLimit); require(_minMembershipRateLimit <= _maxMembershipRateLimit); require(_maxMembershipRateLimit <= _maxTotalRateLimit); require(_activeDurationForNewMemberships > 0); diff --git a/test/WakuRlnV2.t.sol b/test/WakuRlnV2.t.sol index a018c5c..7cd54b9 100644 --- a/test/WakuRlnV2.t.sol +++ b/test/WakuRlnV2.t.sol @@ -2,8 +2,7 @@ pragma solidity >=0.8.19 <0.9.0; import { Test } from "forge-std/Test.sol"; -import { Deploy } from "../script/Deploy.s.sol"; -import { DeploymentConfig } from "../script/DeploymentConfig.s.sol"; +import { DeployPriceCalculator, DeployWakuRlnV2, DeployProxy } from "../script/Deploy.s.sol"; import "../src/WakuRlnV2.sol"; // solhint-disable-line import "../src/Membership.sol"; // solhint-disable-line import { IPriceCalculator } from "../src/IPriceCalculator.sol"; @@ -16,19 +15,19 @@ import "forge-std/console.sol"; contract WakuRlnV2Test is Test { WakuRlnV2 internal w; - address internal impl; - DeploymentConfig internal deploymentConfig; TestToken internal token; address internal deployer; - uint256[] noIdCommitmentsToErase = new uint256[](0); + uint256[] internal noIdCommitmentsToErase = new uint256[](0); function setUp() public virtual { token = new TestToken(); + IPriceCalculator priceCalculator = (new DeployPriceCalculator()).deploy(address(token)); + WakuRlnV2 wakuRlnV2 = (new DeployWakuRlnV2()).deploy(); + ERC1967Proxy proxy = (new DeployProxy()).deploy(address(priceCalculator), address(wakuRlnV2)); - Deploy deployment = new Deploy(); - (w, impl) = deployment.deploy(address(token)); + w = WakuRlnV2(address(proxy)); // Minting a large number of tokens to not have to worry about // Not having enough balance @@ -61,8 +60,11 @@ contract WakuRlnV2Test is Test { w.root(), 13_801_897_483_540_040_307_162_267_952_866_411_686_127_372_014_953_358_983_481_592_640_000_001_877_295 ); - (uint32 membershipRateLimit2, uint32 index2, uint256 rateCommitment2) = w.getMembershipInfo(idCommitment); - assertEq(membershipRateLimit2, membershipRateLimit); + uint32 fetchedMembershipRateLimit2; + uint32 index2; + uint256 rateCommitment2; + (fetchedMembershipRateLimit2, index2, rateCommitment2) = w.getMembershipInfo(idCommitment); + assertEq(fetchedMembershipRateLimit2, membershipRateLimit); assertEq(index2, 0); assertEq(rateCommitment2, rateCommitment); uint256[20] memory proof = w.getMerkleProof(0);