chore: introduce foundry deployment script (#75)

This introduces a foundry based deployment script which is also used in
tests so the deployment itself is tested as well.
This commit is contained in:
r4bbit 2023-10-04 11:05:33 +02:00 committed by GitHub
parent e6dec47f02
commit 086601d25b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 464 additions and 186 deletions

View File

@ -9,10 +9,21 @@ Proposed api is available [here](API.md)
# Running the project
Note: Use node `v18.15.0`
NOte: Use Foundry ([getfoundry.sh](https://getfoundry.sh))
1. Install dependencies: Run `yarn` to install the necessary packages and dependencies.
2. Run local hardhat node: Navigate to the `packages/contract` directory and run `yarn dev`.
3. Deploy contracts: Navigate to the `packages/contracts` directory and run `yarn dev:deploy` to deploy the contracts needed for the project.
2. Run local anvil node: Run `anvil`
3. Deploy contracts: Navigate to the `packages/contracts` directory and run:
```
$ MNEMONIC=$YOUR_MNEMONIC forge script script/DeployContracts.s.sol --fork-url $YOUR_RPC_URL --broadcast
```
Where
- `$YOUR_MNEMONIC` is the mnemonic that contains the account from which you want to deploy. The deploy script will use the first account derived from the mnemonic by default.
- `$YOUR_RPC_URL` is the RPC endpoint of the node you're connecting to.
You can omit the `--broadcast` option to simulate the deployment before actually performing it.
4. Start the app: Run `VOTING_CONTRACT=hex_addr DIRECTORY_CONTRACT=hex_addr MULTICALL_CONTRACT=hex_addr TOKEN_CONTRACT=hex_addr FEATURED_VOTING_CONTRACT=hex_addr yarn dev` to start the application. Make sure to replace `hex_addr` with the actual addresses of the contracts you deployed in step 3.
Once the app is run, connect to the wallet. NOTE: in 'production' mode it has to be Status wallet.

View File

@ -1,54 +1,54 @@
AddCommunity_Test:test_AddCommunity() (gas: 91041)
AddCommunity_Test:test_RevertWhen_CommunityAlreadyExists() (gas: 85002)
AddCommunity_Test:test_RevertWhen_SenderIsNotVotingContract() (gas: 13410)
CastVotes_Test:test_CastVotes() (gas: 1532595)
CastVotes_Test:test_CastVotes_CooldownPeriodHasNotPassed() (gas: 1291110)
CastVotes_Test:test_CastVotes_EmitAlreadyVoted() (gas: 755382)
CastVotes_Test:test_CastVotes_EmitInvalidSignature() (gas: 495563)
CastVotes_Test:test_CastVotes_EmitInvalidSignature() (gas: 524662)
CastVotes_Test:test_CastVotes_EmitInvalidVoteTimestamp() (gas: 554518)
CastVotes_Test:test_CastVotes_EmitNotEnoughToken() (gas: 558484)
CastVotes_Test:test_RevertWhen_InvalidVoteTimestamp() (gas: 510358)
CastVotes_Test:test_RevertWhen_NoOngoingVote() (gas: 11301)
CastVotes_Test:test_RevertWhen_VotingHasBeenClosedAlready() (gas: 529574)
CastVotes_Test:test_RevertWhen_VotingRoomHasBeenClosedAlready() (gas: 496107)
FinalizeVotingRoom_Test:test_FinalizeVotingRoom() (gas: 638140)
FinalizeVotingRoom_Test:test_FinalizeVotingRoom_ShouldIgnoreVoteIfVoterHasInsufficientFunds() (gas: 512591)
FinalizeVotingRoom_Test:test_RevertWhen_VoteAlreadyFinalized() (gas: 601084)
FinalizeVotingRoom_Test:test_RevertWhen_VoteStillOngoing() (gas: 471919)
FinalizeVoting_Test:test_FinalizeVoting() (gas: 781023)
FinalizeVoting_Test:test_FinalizeVoting_FeatureByFirstCastedWhenVoteIsADraw() (gas: 2539836)
FinalizeVoting_Test:test_RevertWhen_NoOngoingVote() (gas: 10875)
FinalizeVoting_Test:test_RevertWhen_VoteStillOngoing() (gas: 495012)
GetActiveVotingRoom_Test:test_GetActiveVotingRoom() (gas: 492958)
GetActiveVotingRooms_Test:test_GetActiveVotingRooms() (gas: 471585)
GetCommunities_Test:test_GetCommunities() (gas: 89087)
GetFeaturedCommunities_Test:test_GetFeaturedCommunities() (gas: 271705)
GetVotesByVotingId_Test:test_GetVotesByVotingId() (gas: 1400379)
GetVotingHistory_Test:test_GetVotingHistory() (gas: 1043307)
GetVotings_Test:test_GetVotings() (gas: 1359622)
InitializeVotingRoom_Test:test_InitializeVotingRoom() (gas: 505260)
InitializeVotingRoom_Test:test_RevertWhen_CommunityIsAlreadyInDirectory() (gas: 164195)
InitializeVotingRoom_Test:test_RevertWhen_CommunityIsNotInDirectory() (gas: 28421)
InitializeVotingRoom_Test:test_RevertWhen_SenderHasNotEnoughFunds() (gas: 40740)
InitializeVotingRoom_Test:test_RevertWhen_TimeBetweenVotingHasNotPassed() (gas: 607299)
InitializeVotingRoom_Test:test_RevertWhen_VoteAlreadyOngoing() (gas: 472694)
InitializeVoting_Test:test_InitializeVoting() (gas: 500530)
InitializeVoting_Test:test_InitializeVoting_CooldownPeriodHasPassed() (gas: 1657044)
InitializeVoting_Test:test_RevertWhen_CommunityIsNotInDirectory() (gas: 28069)
InitializeVoting_Test:test_RevertWhen_CooldownPeriodHasNotPassed() (gas: 803526)
InitializeVoting_Test:test_RevertWhen_SenderHasNotEnoughFunds() (gas: 178491)
InitializeVoting_Test:test_RevertWhen_VoteAlreadyOngoing() (gas: 495687)
IsCommunityFeatured_Test:test_IsCommunityFeatured() (gas: 213907)
IsCommunityInDirectory_Test:test_IsInCommunityDirectory() (gas: 84208)
IsInCooldownPeriod_Test:test_IsInCooldownPeriod() (gas: 1382892)
ListRoomVoters_Test:test_ListRoomVoters() (gas: 645736)
RemoveCommunity_Test:test_RemoveCommunity() (gas: 158785)
RemoveCommunity_Test:test_RemoveCommunity_ShouldRemoveFeaturedCommunity() (gas: 303330)
RemoveCommunity_Test:test_RevertWhen_SenderIsNotVotingContract() (gas: 84707)
SetDirectory_Test:test_RevertWhen_NotOwner() (gas: 15429)
SetDirectory_Test:test_RevertWhen_SenderIsNotOwner() (gas: 15395)
SetFeaturedCommunities_Test:test_RevertWhen_CommunitiesAreNotInDirectory() (gas: 21956)
SetFeaturedCommunities_Test:test_RevertWhen_SenderIsNotFeaturedVotingContract() (gas: 23331)
SetFeaturedCommunities_Test:test_SetFeaturedCommunities() (gas: 363814)
SetFeaturedCommunities_Test:test_SetFeaturedCommunities_ShouldOverridePreviousFeaturedCommunities() (gas: 541785)
AddCommunityTest:test_AddCommunity() (gas: 91041)
AddCommunityTest:test_RevertWhen_CommunityAlreadyExists() (gas: 85002)
AddCommunityTest:test_RevertWhen_SenderIsNotVotingContract() (gas: 13410)
CastVotesTest:test_CastVotes() (gas: 1695374)
CastVotesTest:test_CastVotes_CooldownPeriodHasNotPassed() (gas: 1265131)
CastVotesTest:test_CastVotes_EmitAlreadyVoted() (gas: 917723)
CastVotesTest:test_CastVotes_EmitInvalidSignature() (gas: 498047)
CastVotesTest:test_CastVotes_EmitInvalidSignature() (gas: 530077)
CastVotesTest:test_CastVotes_EmitInvalidVoteTimestamp() (gas: 557923)
CastVotesTest:test_CastVotes_EmitNotEnoughToken() (gas: 657295)
CastVotesTest:test_RevertWhen_InvalidVoteTimestamp() (gas: 510886)
CastVotesTest:test_RevertWhen_NoOngoingVote() (gas: 11304)
CastVotesTest:test_RevertWhen_VotingHasBeenClosedAlready() (gas: 532747)
CastVotesTest:test_RevertWhen_VotingRoomHasBeenClosedAlready() (gas: 496601)
FinalizeVotingRoomTest:test_FinalizeVotingRoom() (gas: 640678)
FinalizeVotingRoomTest:test_FinalizeVotingRoom_ShouldIgnoreVoteIfVoterHasInsufficientFunds() (gas: 522459)
FinalizeVotingRoomTest:test_RevertWhen_VoteAlreadyFinalized() (gas: 603580)
FinalizeVotingRoomTest:test_RevertWhen_VoteStillOngoing() (gas: 474403)
FinalizeVotingTest:test_FinalizeVoting() (gas: 785963)
FinalizeVotingTest:test_FinalizeVoting_FeatureByFirstCastedWhenVoteIsADraw() (gas: 2514768)
FinalizeVotingTest:test_RevertWhen_NoOngoingVote() (gas: 10875)
FinalizeVotingTest:test_RevertWhen_VoteStillOngoing() (gas: 499952)
GetActiveVotingRoomTest:test_GetActiveVotingRoom() (gas: 497970)
GetActiveVotingRoomsTest:test_GetActiveVotingRooms() (gas: 476727)
GetCommunitiesTest:test_GetCommunities() (gas: 89087)
GetFeaturedCommunitiesTest:test_GetFeaturedCommunities() (gas: 271705)
GetVotesByVotingIdTest:test_GetVotesByVotingId() (gas: 1406847)
GetVotingHistoryTest:test_GetVotingHistory() (gas: 1046389)
GetVotingsTest:test_GetVotings() (gas: 1365680)
InitializeVotingRoomTest:test_InitializeVotingRoom() (gas: 507941)
InitializeVotingRoomTest:test_RevertWhen_CommunityIsAlreadyInDirectory() (gas: 166703)
InitializeVotingRoomTest:test_RevertWhen_CommunityIsNotInDirectory() (gas: 30893)
InitializeVotingRoomTest:test_RevertWhen_SenderHasNotEnoughFunds() (gas: 40764)
InitializeVotingRoomTest:test_RevertWhen_TimeBetweenVotingHasNotPassed() (gas: 610255)
InitializeVotingRoomTest:test_RevertWhen_VoteAlreadyOngoing() (gas: 475650)
InitializeVotingTest:test_InitializeVoting() (gas: 505527)
InitializeVotingTest:test_InitializeVoting_CooldownPeriodHasPassed() (gas: 1663769)
InitializeVotingTest:test_RevertWhen_CommunityIsNotInDirectory() (gas: 30324)
InitializeVotingTest:test_RevertWhen_CooldownPeriodHasNotPassed() (gas: 809242)
InitializeVotingTest:test_RevertWhen_SenderHasNotEnoughFunds() (gas: 181022)
InitializeVotingTest:test_RevertWhen_VoteAlreadyOngoing() (gas: 500918)
IsCommunityFeaturedTest:test_IsCommunityFeatured() (gas: 213907)
IsCommunityInDirectoryTest:test_IsInCommunityDirectory() (gas: 84208)
IsInCooldownPeriodTest:test_IsInCooldownPeriod() (gas: 1389411)
ListRoomVotersTest:test_ListRoomVoters() (gas: 716899)
RemoveCommunityTest:test_RemoveCommunity() (gas: 158785)
RemoveCommunityTest:test_RemoveCommunity_ShouldRemoveFeaturedCommunity() (gas: 303330)
RemoveCommunityTest:test_RevertWhen_SenderIsNotVotingContract() (gas: 84707)
SetDirectoryTest:test_RevertWhen_NotOwner() (gas: 15456)
SetDirectoryTest:test_RevertWhen_SenderIsNotOwner() (gas: 15422)
SetFeaturedCommunitiesTest:test_RevertWhen_CommunitiesAreNotInDirectory() (gas: 21956)
SetFeaturedCommunitiesTest:test_RevertWhen_SenderIsNotFeaturedVotingContract() (gas: 23331)
SetFeaturedCommunitiesTest:test_SetFeaturedCommunities() (gas: 363814)
SetFeaturedCommunitiesTest:test_SetFeaturedCommunities_ShouldOverridePreviousFeaturedCommunities() (gas: 541785)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,41 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <=0.9.0;
import { Script } from "forge-std/Script.sol";
abstract contract BaseScript is Script {
/// @dev Included to enable compilation of the script without a $MNEMONIC environment variable.
string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk";
/// @dev Needed for the deterministic deployments.
bytes32 internal constant ZERO_SALT = bytes32(0);
/// @dev The address of the transaction broadcaster.
address internal broadcaster;
/// @dev Used to derive the broadcaster's address if $ETH_FROM is not defined.
string internal mnemonic;
/// @dev Initializes the transaction broadcaster like this:
///
/// - If $ETH_FROM is defined, use it.
/// - Otherwise, derive the broadcaster address from $MNEMONIC.
/// - If $MNEMONIC is not defined, default to a test mnemonic.
///
/// The use case for $ETH_FROM is to specify the broadcaster key and its address via the command line.
constructor() {
address from = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) });
if (from != address(0)) {
broadcaster = from;
} else {
mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC });
(broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 });
}
}
modifier broadcast() {
vm.startBroadcast(broadcaster);
_;
vm.stopBroadcast();
}
}

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import { Script } from "forge-std/Script.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { DeploymentConfig } from "./DeploymentConfig.s.sol";
import { BaseScript } from "./Base.s.sol";
import { Directory } from "../contracts/Directory.sol";
import { FeaturedVotingContract } from "../contracts/FeaturedVotingContract.sol";
import { VotingContract } from "../contracts/VotingContract.sol";
contract DeployContracts is BaseScript {
function run() external returns (Directory, VotingContract, FeaturedVotingContract, DeploymentConfig) {
DeploymentConfig deploymentConfig = new DeploymentConfig(broadcaster);
(
uint32 votingLengthInSeconds,
uint32 votingVerificationLength,
uint32 timeBetweenVotingInSeconds,
uint32 featuredVotingLength,
uint32 featuredVotingVerificationLength,
uint8 cooldownPeriod,
uint8 featuredPerVotingCount,
address tokenAddress
) = deploymentConfig.activeNetworkConfig();
vm.startBroadcast(broadcaster);
VotingContract votingContract = new VotingContract(
IERC20(tokenAddress),
votingLengthInSeconds,
votingVerificationLength,
timeBetweenVotingInSeconds
);
FeaturedVotingContract featuredVotingContract = new FeaturedVotingContract(
IERC20(tokenAddress),
featuredVotingLength,
featuredVotingVerificationLength,
cooldownPeriod,
featuredPerVotingCount
);
Directory directoryContract = new Directory(address(votingContract), address(featuredVotingContract));
vm.stopBroadcast();
return (directoryContract, votingContract, featuredVotingContract, deploymentConfig);
}
}

View File

@ -0,0 +1,90 @@
//// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import { Script } from "forge-std/Script.sol";
import { MockSNT } from "../test/mocks/MockSNT.sol";
contract DeploymentConfig is Script {
NetworkConfig public activeNetworkConfig;
struct NetworkConfig {
uint32 votingLengthInSeconds;
uint32 votingVerificationLengthInSeconds;
uint32 timeBetweenVotingInSeconds;
uint32 featuredVotingLengthInSeconds;
uint32 featuredVotingVerificationLengthInSeconds;
uint8 cooldownPeriod;
uint8 featuredPerVotingCount;
address voteToken;
}
uint32 internal constant ONE_DAY_IN_SECONDS = 24 * 3600;
uint32 internal constant TWO_DAYS_IN_SECONDS = 2 * 24 * 3600;
uint32 internal constant FOUR_MINS_IN_SECONDS = 4 * 60;
uint32 internal constant TWO_MINS_IN_SECONDS = 2 * 60;
uint32 internal constant ONE_MIN_IN_SECONDS = 60;
address public deployer;
// solhint-disable-next-line var-name-mixedcase
address internal SNT_ADDRESS_GOERLI = 0x3D6AFAA395C31FCd391fE3D562E75fe9E8ec7E6a;
// solhint-disable-next-line var-name-mixedcase
address internal SNT_ADDRESS_MAINNET = 0x744d70FDBE2Ba4CF95131626614a1763DF805B9E;
constructor(address _broadcaster) {
if (block.chainid == 1) {
activeNetworkConfig = getMainnetConfig();
} else if (block.chainid == 5) {
activeNetworkConfig = getGoerliEthConfig();
} else if (block.chainid == 31_337) {
activeNetworkConfig = getOrCreateAnvilEthConfig();
} else {
revert("no network config for this chain");
}
deployer = _broadcaster;
}
function getMainnetConfig() public view returns (NetworkConfig memory) {
return NetworkConfig({
votingLengthInSeconds: TWO_DAYS_IN_SECONDS,
votingVerificationLengthInSeconds: ONE_DAY_IN_SECONDS,
timeBetweenVotingInSeconds: TWO_DAYS_IN_SECONDS,
featuredVotingLengthInSeconds: TWO_DAYS_IN_SECONDS,
featuredVotingVerificationLengthInSeconds: ONE_DAY_IN_SECONDS,
cooldownPeriod: 2,
featuredPerVotingCount: 3,
voteToken: SNT_ADDRESS_MAINNET
});
}
function getGoerliEthConfig() public view returns (NetworkConfig memory) {
return NetworkConfig({
votingLengthInSeconds: FOUR_MINS_IN_SECONDS,
votingVerificationLengthInSeconds: TWO_MINS_IN_SECONDS,
timeBetweenVotingInSeconds: ONE_MIN_IN_SECONDS,
featuredVotingLengthInSeconds: FOUR_MINS_IN_SECONDS,
featuredVotingVerificationLengthInSeconds: TWO_MINS_IN_SECONDS,
cooldownPeriod: 1,
featuredPerVotingCount: 3,
voteToken: SNT_ADDRESS_GOERLI
});
}
function getOrCreateAnvilEthConfig() public returns (NetworkConfig memory) {
vm.startBroadcast();
MockSNT voteToken = new MockSNT();
vm.stopBroadcast();
return NetworkConfig({
votingLengthInSeconds: FOUR_MINS_IN_SECONDS,
votingVerificationLengthInSeconds: TWO_MINS_IN_SECONDS,
timeBetweenVotingInSeconds: ONE_MIN_IN_SECONDS,
featuredVotingLengthInSeconds: FOUR_MINS_IN_SECONDS,
featuredVotingVerificationLengthInSeconds: TWO_MINS_IN_SECONDS,
cooldownPeriod: 1,
featuredPerVotingCount: 3,
voteToken: address(voteToken)
});
}
}

View File

@ -2,19 +2,27 @@
pragma solidity ^0.8.19;
import { Test } from "forge-std/Test.sol";
import { DeployContracts } from "../script/DeployContracts.s.sol";
import { Directory } from "../contracts/Directory.sol";
import { VotingContract } from "../contracts/VotingContract.sol";
import { FeaturedVotingContract } from "../contracts/FeaturedVotingContract.sol";
contract DirectoryTest is Test {
Directory public directory;
address internal votingContract = makeAddr("votingContract");
address internal featuredVotingContract = makeAddr("featuredVotingContract");
address internal votingContract;
address internal featuredVotingContract;
bytes internal communityID = "communityID";
bytes internal communityID2 = "communityID2";
bytes internal communityID3 = "communityID3";
function setUp() public virtual {
directory = new Directory(votingContract, featuredVotingContract);
DeployContracts deployment = new DeployContracts();
(Directory _directory, VotingContract _votingContract, FeaturedVotingContract _featuredVotingContract,) =
deployment.run();
directory = _directory;
votingContract = address(_votingContract);
featuredVotingContract = address(_featuredVotingContract);
}
function _addCommunitiesToDirectory(bytes[] memory communityIDs) internal {

View File

@ -2,26 +2,21 @@
pragma solidity ^0.8.19;
import { Test } from "forge-std/Test.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { DeployContracts } from "../script/DeployContracts.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { SigUtils } from "./SigUtils.sol";
import { Directory } from "../contracts/Directory.sol";
import { VotingContract } from "../contracts/VotingContract.sol";
import { FeaturedVotingContract } from "../contracts/FeaturedVotingContract.sol";
contract MockSNT is ERC20 {
constructor() ERC20("Mock SNT", "MSNT") {
_mint(msg.sender, 1_000_000_000_000_000_000);
}
}
// solhint-disable-next-line max-states-count
contract FeaturedVotingContractTest is Test {
uint256 internal votingLength = 1000;
uint256 internal votingVerificationLength = 200;
uint256 internal featuredPerVotingCount = 3;
uint256 internal cooldownPeriod = 1;
uint256 internal votingWithVerificationLength = votingLength + votingVerificationLength;
uint32 internal featuredVotingLengthInSeconds;
uint32 internal featuredVotingVerificationLengthInSeconds;
uint256 internal votingWithVerificationLength;
address internal votingContract = makeAddr("votingContract");
address internal votingContract;
address internal bob;
uint256 internal bobsKey;
@ -40,7 +35,6 @@ contract FeaturedVotingContractTest is Test {
SigUtils internal sigUtils;
MockSNT internal mockSNT;
Directory internal directoryContract;
FeaturedVotingContract internal featuredVotingContract;
@ -119,16 +113,34 @@ contract FeaturedVotingContractTest is Test {
}
function setUp() public virtual {
mockSNT = new MockSNT();
DeployContracts deployment = new DeployContracts();
(
Directory _directory,
VotingContract _votingContract,
FeaturedVotingContract _featuredVotingContract,
DeploymentConfig deploymentConfig
) = deployment.run();
featuredVotingContract = new FeaturedVotingContract(
mockSNT,
votingLength,
votingVerificationLength,
cooldownPeriod,
featuredPerVotingCount
);
directoryContract = new Directory(votingContract, address(featuredVotingContract));
(
,
,
,
uint32 _featuredVotingLengthInSeconds,
uint32 _featuredVotingVerificationLengthInSeconds,
,
,
address mockSNT
) = deploymentConfig.activeNetworkConfig();
votingContract = address(_votingContract);
featuredVotingLengthInSeconds = _featuredVotingLengthInSeconds;
featuredVotingVerificationLengthInSeconds = _featuredVotingVerificationLengthInSeconds;
votingWithVerificationLength = featuredVotingLengthInSeconds + featuredVotingVerificationLengthInSeconds;
featuredVotingContract = _featuredVotingContract;
directoryContract = _directory;
vm.prank(deploymentConfig.deployer());
featuredVotingContract.setDirectory(directoryContract);
DOMAIN_SEPARATOR = _hashDomainData(block.chainid, address(featuredVotingContract));
@ -148,6 +160,8 @@ contract FeaturedVotingContractTest is Test {
communityIDs[3] = bytes("0xadfcf42e083e71d8c755da07a2b1bad754d7ca97c35fbd407da6bde9844580ad55");
communityIDs[4] = bytes("0xec62724b6828954a705eb3b531c30a69503d3561d4283fb8b60835ff34205c64d8");
communityIDs[5] = bytes("0xb8def1f5e7160e5e1a6440912b7e633ad923030352f23abb54226020bff781b7e6");
deal(mockSNT, bob, 100_000);
}
}
@ -172,6 +186,7 @@ contract InitializeVotingTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.startPrank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
vm.expectRevert(bytes("vote already ongoing"));
featuredVotingContract.initializeVoting(communityIDs[0], 100);
@ -195,11 +210,13 @@ contract InitializeVotingTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
vm.expectRevert(bytes("community has been featured recently"));
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
}
@ -209,13 +226,14 @@ contract InitializeVotingTest is FeaturedVotingContractTest {
vm.expectEmit(false, false, false, false);
emit VotingStarted();
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
FeaturedVotingContract.Voting memory voting = _getVoting(0);
assertEq(voting.id, 1);
assertEq(voting.startBlock, block.number);
assertEq(voting.startAt, block.timestamp);
assertEq(voting.verificationStartAt, block.timestamp + votingLength);
assertEq(voting.verificationStartAt, block.timestamp + featuredVotingLengthInSeconds);
assertEq(voting.endAt, block.timestamp + votingWithVerificationLength);
assertFalse(voting.finalized);
}
@ -224,6 +242,7 @@ contract InitializeVotingTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
@ -233,11 +252,13 @@ contract InitializeVotingTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[1]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[1], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
// try initialize voting that was already featured
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
}
}
@ -256,6 +277,7 @@ contract FinalizeVotingTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
vm.expectRevert(bytes("vote still ongoing"));
featuredVotingContract.finalizeVoting();
@ -265,6 +287,7 @@ contract FinalizeVotingTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
vm.expectEmit(false, false, false, false);
@ -286,6 +309,7 @@ contract FinalizeVotingTest is FeaturedVotingContractTest {
directoryContract.addCommunity(communityIDs[5]);
vm.stopPrank();
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[2], 100);
FeaturedVotingContract.SignedVote[] memory votes = new FeaturedVotingContract.SignedVote[](4);
@ -294,9 +318,6 @@ contract FinalizeVotingTest is FeaturedVotingContractTest {
votes[2] = _createSignedVote(bobsKey, bob, communityIDs[1], 100, block.timestamp);
votes[3] = _createSignedVote(bobsKey, bob, communityIDs[5], 100, block.timestamp);
// ensure bob has funds
mockSNT.transfer(bob, 10_000);
featuredVotingContract.castVotes(votes);
skip(votingWithVerificationLength + 1);
@ -333,6 +354,7 @@ contract CastVotesTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
@ -345,6 +367,7 @@ contract CastVotesTest is FeaturedVotingContractTest {
function test_CastVotes_EmitInvalidSignature() public {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
FeaturedVotingContract.SignedVote[] memory votes = new FeaturedVotingContract.SignedVote[](1);
@ -361,6 +384,7 @@ contract CastVotesTest is FeaturedVotingContractTest {
directoryContract.addCommunity(communityIDs[0]);
// initialize and finalize first voting for communityIDs[0]
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
@ -377,6 +401,7 @@ contract CastVotesTest is FeaturedVotingContractTest {
directoryContract.addCommunity(communityIDs[1]);
// initialize second voting not containing communityIDs[0]
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[1], 100);
// cast a vote for `communityIDs[0]` to have `castVotes()` emit the expected
@ -384,9 +409,6 @@ contract CastVotesTest is FeaturedVotingContractTest {
FeaturedVotingContract.SignedVote[] memory votes = new FeaturedVotingContract.SignedVote[](1);
votes[0] = _createSignedVote(bobsKey, bob, communityIDs[0], 200, block.timestamp);
// ensure bob has funds
mockSNT.transfer(bob, 10_000);
vm.expectEmit(false, false, false, true);
emit CommunityFeaturedRecently(communityIDs[0], bob);
featuredVotingContract.castVotes(votes);
@ -403,6 +425,7 @@ contract CastVotesTest is FeaturedVotingContractTest {
// fast forward, such that vote timestamp will be earlier than the
// voting room's startAt
skip(1000);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
vm.expectRevert(bytes("invalid vote timestamp"));
@ -410,7 +433,7 @@ contract CastVotesTest is FeaturedVotingContractTest {
// now create another failing vote that has a newer timestamp than the
// voting room's verificationStartAt
skip(votingLength + 1);
skip(featuredVotingLengthInSeconds + 1);
votes[0] = _createSignedVote(bobsKey, bob, communityIDs[0], 200, block.timestamp);
vm.expectRevert(bytes("invalid vote timestamp"));
featuredVotingContract.castVotes(votes);
@ -428,10 +451,12 @@ contract GetVotingsTest is FeaturedVotingContractTest {
directoryContract.addCommunity(communityIDs[1]);
vm.stopPrank();
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[1], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
@ -458,10 +483,12 @@ contract GetVotesByVotingIdTest is FeaturedVotingContractTest {
directoryContract.addCommunity(communityIDs[1]);
vm.stopPrank();
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[1], 300);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
@ -487,6 +514,7 @@ contract IsInCooldownPeriodTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[0]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[0], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();
@ -494,6 +522,7 @@ contract IsInCooldownPeriodTest is FeaturedVotingContractTest {
vm.prank(votingContract);
directoryContract.addCommunity(communityIDs[1]);
vm.prank(bob);
featuredVotingContract.initializeVoting(communityIDs[1], 100);
skip(votingWithVerificationLength + 1);
featuredVotingContract.finalizeVoting();

View File

@ -2,27 +2,23 @@
pragma solidity ^0.8.19;
import { Test } from "forge-std/Test.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { DeployContracts } from "../script/DeployContracts.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { SigUtils } from "./SigUtils.sol";
import { Directory } from "../contracts/Directory.sol";
import { VotingContract } from "../contracts/VotingContract.sol";
contract MockSNT is ERC20 {
constructor() ERC20("Mock SNT", "MSNT") {
_mint(msg.sender, 1_000_000_000_000_000_000);
}
}
contract VotingContractTest is Test {
uint256 internal votingLength = 1000;
uint256 internal votingVerificationLength = 200;
uint256 internal timeBetweenVoting = 3600;
uint256 internal votingWithVerificationLength = votingLength + votingVerificationLength;
uint256 internal votingLength;
uint256 internal votingVerificationLength;
uint256 internal timeBetweenVoting;
uint256 internal votingWithVerificationLength;
VotingContract internal votingContract;
Directory internal directoryContract;
MockSNT internal mockSNT;
SigUtils internal sigUtils;
IERC20 mockSNT;
address internal bob;
uint256 internal bobsKey;
@ -106,10 +102,29 @@ contract VotingContractTest is Test {
}
function setUp() public virtual {
mockSNT = new MockSNT();
address featuredVotingContract = makeAddr("featuredVotingContract");
votingContract = new VotingContract(mockSNT, votingLength, votingVerificationLength, timeBetweenVoting);
directoryContract = new Directory(address(votingContract), featuredVotingContract);
DeployContracts deployment = new DeployContracts();
(Directory _directory, VotingContract _votingContract,, DeploymentConfig deploymentConfig) = deployment.run();
(
uint32 _votingLengthInSeconds,
uint32 _votingVerificationLengthInSeconds,
uint32 _timeBetweenVoting,
,
,
,
,
address tokenAddress
) = deploymentConfig.activeNetworkConfig();
timeBetweenVoting = _timeBetweenVoting;
votingContract = _votingContract;
directoryContract = _directory;
votingLength = _votingLengthInSeconds;
votingVerificationLength = _votingVerificationLengthInSeconds;
votingWithVerificationLength = votingLength + votingVerificationLength;
mockSNT = IERC20(tokenAddress);
vm.prank(deploymentConfig.deployer());
votingContract.setDirectory(directoryContract);
DOMAIN_SEPARATOR = _hashDomainData(block.chainid, address(votingContract));
@ -122,7 +137,7 @@ contract VotingContractTest is Test {
alice = alice_;
alicesKey = alicesKey_;
mockSNT.transfer(bob, 10_000);
deal(address(mockSNT), bob, 100_000);
}
}
@ -144,6 +159,7 @@ contract GetActiveVotingRoomTest is VotingContractTest {
}
function test_GetActiveVotingRoom() public {
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
VotingContract.VotingRoom memory votingRoom = votingContract.getActiveVotingRoom(communityID1);
@ -165,6 +181,7 @@ contract GetActiveVotingRoomsTest is VotingContractTest {
uint256[] memory votingRooms = votingContract.getActiveVotingRooms();
assertEq(votingRooms.length, 0);
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
votingRooms = votingContract.getActiveVotingRooms();
@ -194,7 +211,7 @@ contract ListRoomVotersTest is VotingContractTest {
votes[1] = _createSignedVote(alicesKey, alice, 1, VotingContract.VoteType.AGAINST, 100, block.timestamp);
// ensure alice has funds
mockSNT.transfer(alice, 1000);
deal(address(mockSNT), alice, 1000);
votingContract.castVotes(votes);
roomVoters = votingContract.listRoomVoters(1);
@ -213,6 +230,7 @@ contract GetVotingHistoryTest is VotingContractTest {
VotingContract.VotingRoom[] memory votingRooms = votingContract.getVotingHistory(communityID1);
assertEq(votingRooms.length, 0);
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
votingRooms = votingContract.getVotingHistory(communityID1);
@ -229,6 +247,7 @@ contract GetVotingHistoryTest is VotingContractTest {
// make sure enough time has passed to initialize another voting room
skip(timeBetweenVoting + 1);
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.AGAINST, communityID1, 200);
votingRooms = votingContract.getVotingHistory(communityID1);
@ -251,8 +270,10 @@ contract InitializeVotingRoomTest is VotingContractTest {
}
function test_RevertWhen_VoteAlreadyOngoing() public {
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
vm.expectRevert(bytes("vote already ongoing"));
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
}
@ -260,6 +281,7 @@ contract InitializeVotingRoomTest is VotingContractTest {
// initializing a voting room with VoteType.AGAINST requires `publicKey`
// to be in the directory
vm.expectRevert(bytes("Community not in directory"));
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.AGAINST, communityID1, 100);
}
@ -267,6 +289,7 @@ contract InitializeVotingRoomTest is VotingContractTest {
vm.prank(address(votingContract));
directoryContract.addCommunity(communityID1);
vm.expectRevert(bytes("Community already in directory"));
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
}
@ -279,6 +302,7 @@ contract InitializeVotingRoomTest is VotingContractTest {
function test_RevertWhen_TimeBetweenVotingHasNotPassed() public {
// vote community `communityID1` into the directory
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
// fast forward such that voting time has passed
skip(votingWithVerificationLength + 1);
@ -287,10 +311,12 @@ contract InitializeVotingRoomTest is VotingContractTest {
vm.expectRevert(bytes("Community was in a vote recently"));
// community is in the directory now, so cause the expected revert
// we need to initilalize a voting room to vote the community out of the directory
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.AGAINST, communityID1, 100);
}
function test_InitializeVotingRoom() public {
vm.prank(bob);
vm.expectEmit(false, false, false, true);
emit VotingRoomStarted(1, communityID1);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
@ -309,7 +335,7 @@ contract InitializeVotingRoomTest is VotingContractTest {
address[] memory roomVoters = votingContract.listRoomVoters(1);
assertEq(roomVoters.length, 1);
assertEq(roomVoters[0], address(this));
assertEq(roomVoters[0], bob);
}
}
@ -319,12 +345,14 @@ contract FinalizeVotingRoomTest is VotingContractTest {
}
function test_RevertWhen_VoteStillOngoing() public {
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
vm.expectRevert(bytes("vote still ongoing"));
votingContract.finalizeVotingRoom(1);
}
function test_RevertWhen_VoteAlreadyFinalized() public {
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
skip(votingWithVerificationLength + 1);
votingContract.finalizeVotingRoom(1);
@ -334,6 +362,7 @@ contract FinalizeVotingRoomTest is VotingContractTest {
function test_FinalizeVotingRoom() public {
uint256 startAt = block.timestamp;
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
skip(votingWithVerificationLength + 1);
@ -362,7 +391,7 @@ contract FinalizeVotingRoomTest is VotingContractTest {
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
// remove bob's funds
mockSNT.transfer(address(mockSNT), mockSNT.balanceOf(bob));
deal(address(mockSNT), bob, 0);
assertEq(mockSNT.balanceOf(bob), 0);
// fast forward to finalize vote
@ -392,6 +421,7 @@ contract CastVotesTest is VotingContractTest {
}
function test_RevertWhen_VotingRoomHasBeenClosedAlready() public {
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
skip(votingWithVerificationLength + 1);
@ -409,6 +439,7 @@ contract CastVotesTest is VotingContractTest {
// fast forward, such that vote timestamp will be earlier than the
// voting room's startAt
skip(1000);
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
vm.expectRevert(bytes("invalid vote timestamp"));
@ -423,6 +454,7 @@ contract CastVotesTest is VotingContractTest {
}
function test_CastVotes_EmitInvalidSignature() public {
vm.prank(bob);
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
VotingContract.SignedVote[] memory votes = new VotingContract.SignedVote[](1);
@ -435,6 +467,9 @@ contract CastVotesTest is VotingContractTest {
}
function test_CastVotes_EmitAlreadyVoted() public {
// neither bob, nor alice should initialize the voting in this scenario
deal(address(mockSNT), address(this), 10_000);
vm.prank(address(this));
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
VotingContract.SignedVote[] memory votes = new VotingContract.SignedVote[](1);
@ -457,8 +492,8 @@ contract CastVotesTest is VotingContractTest {
// try voting as alice
votes[0] = _createSignedVote(alicesKey, alice, 1, VotingContract.VoteType.FOR, 200, block.timestamp);
// ensure alice has funds
mockSNT.transfer(alice, 10_000);
// ensure alice has funds (bob already got funds durign `setUp`)
deal(address(mockSNT), alice, 10_000);
votingContract.castVotes(votes);
// check if voting wen through
@ -467,14 +502,16 @@ contract CastVotesTest is VotingContractTest {
}
function test_CastVotes_EmitNotEnoughToken() public {
// neither bob, nor alice should initialize the voting in this scenario
deal(address(mockSNT), address(this), 10_000);
vm.prank(address(this));
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
VotingContract.SignedVote[] memory votes = new VotingContract.SignedVote[](1);
votes[0] = _createSignedVote(bobsKey, bob, 1, VotingContract.VoteType.FOR, 200, block.timestamp);
// remove bob's funds
vm.startPrank(bob);
mockSNT.transfer(address(mockSNT), mockSNT.balanceOf(bob));
deal(address(mockSNT), bob, 0);
assertEq(mockSNT.balanceOf(bob), 0);
vm.expectEmit(false, false, false, true);
@ -489,8 +526,11 @@ contract CastVotesTest is VotingContractTest {
function test_CastVotes() public {
// ensure alice has funds
mockSNT.transfer(alice, 1000);
deal(address(mockSNT), alice, 10_000);
// neither bob, nor alice should initialize the voting in this scenario
deal(address(mockSNT), address(this), 10_000);
vm.prank(address(this));
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
VotingContract.SignedVote[] memory votes = new VotingContract.SignedVote[](2);
@ -524,6 +564,7 @@ contract CastVotesTest is VotingContractTest {
// fast forward to initialize a new voting room
skip(timeBetweenVoting + 1);
vm.prank(address(this));
votingContract.initializeVotingRoom(VotingContract.VoteType.FOR, communityID1, 100);
votes[0] = _createSignedVote(bobsKey, bob, 2, VotingContract.VoteType.FOR, 500, block.timestamp);

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockSNT is ERC20 {
constructor() ERC20("Mock SNT", "SNT") {
_mint(msg.sender, 1_000_000_000_000_000_000);
}
}