diff --git a/script/DeployDirectDemocracyOrg.s.sol b/script/DeployDirectDemocracyOrg.s.sol index ecb2cbe..9ba12a6 100644 --- a/script/DeployDirectDemocracyOrg.s.sol +++ b/script/DeployDirectDemocracyOrg.s.sol @@ -52,18 +52,18 @@ contract DeployDirectDemocracyOrg { params.contractNames[3] = "Treasury"; params.contractNames[4] = "DirectDemocracyVoting"; params.contractNames[5] = "TaskManager"; - params.contractNames[6] = "QuickJoin"; // Add ElectionContract if electionEnabled is true - uint256 nextIndex = 7; + uint256 nextIndex = 6; if (electionEnabled) { params.contractNames[nextIndex++] = "ElectionContract"; } // Add EducationHub if educationHubEnabled is true if (educationHubEnabled) { - params.contractNames[nextIndex] = "EducationHub"; + params.contractNames[nextIndex++] = "EducationHub"; } + params.contractNames[nextIndex] = "QuickJoin"; // Deploy the organization and return the registry address address registryAddress = masterFactory.deployAll(params); diff --git a/script/DeployHybridOrg.s.sol b/script/DeployHybridOrg.s.sol index 0cfbd9d..5227a95 100644 --- a/script/DeployHybridOrg.s.sol +++ b/script/DeployHybridOrg.s.sol @@ -49,19 +49,20 @@ contract DeployHybridOrg { params.contractNames[4] = "DirectDemocracyVoting"; params.contractNames[5] = "HybridVoting"; params.contractNames[6] = "TaskManager"; - params.contractNames[7] = "QuickJoin"; // Add ElectionContract if electionEnabled is true - uint256 nextIndex = 8; + uint256 nextIndex = 7; if (electionEnabled) { params.contractNames[nextIndex++] = "ElectionContract"; } // Add EducationHub if educationHubEnabled is true if (educationHubEnabled) { - params.contractNames[nextIndex] = "EducationHub"; + params.contractNames[nextIndex++] = "EducationHub"; } + params.contractNames[nextIndex] = "QuickJoin"; + MasterFactory masterFactory = MasterFactory(_masterFactory); address registryAddress = masterFactory.deployAll(params); return registryAddress; diff --git a/script/DeployParticipationOrg.s.sol b/script/DeployParticipationOrg.s.sol index 7339377..e2d3d71 100644 --- a/script/DeployParticipationOrg.s.sol +++ b/script/DeployParticipationOrg.s.sol @@ -49,19 +49,20 @@ contract DeployParticipationOrg { params.contractNames[4] = "DirectDemocracyVoting"; params.contractNames[5] = "ParticipationVoting"; params.contractNames[6] = "TaskManager"; - params.contractNames[7] = "QuickJoin"; // Add ElectionContract if electionEnabled is true - uint256 nextIndex = 8; + uint256 nextIndex = 7; if (electionEnabled) { params.contractNames[nextIndex++] = "ElectionContract"; } // Add EducationHub if educationHubEnabled is true if (educationHubEnabled) { - params.contractNames[nextIndex] = "EducationHub"; + params.contractNames[nextIndex++] = "EducationHub"; } + params.contractNames[nextIndex] = "QuickJoin"; + MasterFactory masterFactory = MasterFactory(_masterFactory); address registryAddress = masterFactory.deployAll(params); return registryAddress; diff --git a/test/deployAllOrgTypes.t.sol b/test/deployAllOrgTypes.t.sol index 5145382..faad813 100644 --- a/test/deployAllOrgTypes.t.sol +++ b/test/deployAllOrgTypes.t.sol @@ -68,6 +68,14 @@ contract TestAllOrgTypes is Test { checkContractAddresses(directDemocracyRegistryAddress, "DirectDemocracyWithElection"); } + function testDeployDirectDemocracyWithEducation() public { + DeployDirectDemocracyOrg deployDirectDemocracyOrg = new DeployDirectDemocracyOrg(); + + directDemocracyRegistryAddress = deployDirectDemocracyOrg.run(address(masterFactory), false, true); + + checkContractAddresses(directDemocracyRegistryAddress, "DirectDemocracyWithEducation"); + } + function testDeployHybridVotingWithEducationHub() public { DeployHybridOrg deployHybridOrg = new DeployHybridOrg(); @@ -76,6 +84,24 @@ contract TestAllOrgTypes is Test { checkContractAddresses(hybridVotingRegistryAddress, "HybridVotingWithEducationHub"); } + function testDeployHybridVotingWithElectionHub() public { + DeployHybridOrg deployHybridOrg = new DeployHybridOrg(); + hybridVotingRegistryAddress = deployHybridOrg.run(address(masterFactory), true, false); + checkContractAddresses(hybridVotingRegistryAddress, "HybridVotingWithElectionHub"); + } + + function testDeployParticipationVotingWithElection() public { + DeployParticipationOrg deployParticipationOrg = new DeployParticipationOrg(); + participationVotingRegistryAddress = deployParticipationOrg.run(address(masterFactory), true, false); + checkContractAddresses(participationVotingRegistryAddress, "ParticipationVotingWithElection"); + } + + function testDeployParticipationVotingWithEducation() public { + DeployParticipationOrg deployParticipationOrg = new DeployParticipationOrg(); + participationVotingRegistryAddress = deployParticipationOrg.run(address(masterFactory), false, true); + checkContractAddresses(participationVotingRegistryAddress, "ParticipationVotingWithEducation"); + } + function checkContractAddresses(address registryAddress, string memory deploymentType) internal view { Registry registry = Registry(registryAddress); @@ -125,6 +151,19 @@ contract TestAllOrgTypes is Test { assertTrue(directDemocracyVoting != address(0), "DirectDemocracyVoting address is invalid"); assertTrue(electionContract == address(0), "ElectionContract should not be deployed"); assertTrue(educationHub == address(0), "EducationHub should not be deployed"); + } else if ( + keccak256(abi.encodePacked(deploymentType)) == keccak256(abi.encodePacked("HybridVotingWithElectionHub")) + ) { + assertTrue(nftMembership != address(0), "NFTMembership address is invalid"); + assertTrue(directDemocracyToken != address(0), "DirectDemocracyToken address is invalid"); + assertTrue(participationToken != address(0), "ParticipationToken address is invalid"); + assertTrue(treasury != address(0), "Treasury address is invalid"); + assertTrue(hybridVotingAddress != address(0), "HybridVoting address is invalid"); + assertTrue(taskManager != address(0), "TaskManager address is invalid"); + assertTrue(quickJoin != address(0), "QuickJoin address is invalid"); + assertTrue(directDemocracyVoting != address(0), "DirectDemocracyVoting address is invalid"); + assertTrue(electionContract != address(0), "ElectionHub address is invalid"); + assertTrue(educationHub == address(0), "EducationContract should not be deployed"); } else if ( keccak256(abi.encodePacked(deploymentType)) == keccak256(abi.encodePacked("DirectDemocracyWithElection")) ) { @@ -137,6 +176,18 @@ contract TestAllOrgTypes is Test { assertTrue(participationToken != address(0), "ParticipationToken address is invalid"); assertTrue(electionContract != address(0), "ElectionContract address is invalid"); assertTrue(educationHub == address(0), "EducationHub should not be deployed"); + } else if ( + keccak256(abi.encodePacked(deploymentType)) == keccak256(abi.encodePacked("DirectDemocracyWithEducation")) + ) { + assertTrue(nftMembership != address(0), "NFTMembership address is invalid"); + assertTrue(directDemocracyToken != address(0), "DirectDemocracyToken address is invalid"); + assertTrue(treasury != address(0), "Treasury address is invalid"); + assertTrue(directDemocracyVoting != address(0), "DirectDemocracyVoting address is invalid"); + assertTrue(taskManager != address(0), "TaskManager address is invalid"); + assertTrue(quickJoin != address(0), "QuickJoin address is invalid"); + assertTrue(participationToken != address(0), "ParticipationToken address is invalid"); + assertTrue(electionContract == address(0), "ElectionContract should not be deployed"); + assertTrue(educationHub != address(0), "EducationHub address is invalid"); } else if ( keccak256(abi.encodePacked(deploymentType)) == keccak256(abi.encodePacked("HybridVotingWithEducationHub")) ) { @@ -150,6 +201,32 @@ contract TestAllOrgTypes is Test { assertTrue(directDemocracyVoting != address(0), "DirectDemocracyVoting address is invalid"); assertTrue(electionContract == address(0), "ElectionContract should not be deployed"); assertTrue(educationHub != address(0), "EducationHub address is invalid"); + } else if ( + keccak256(abi.encodePacked(deploymentType)) + == keccak256(abi.encodePacked("ParticipationVotingWithEducation")) + ) { + assertTrue(nftMembership != address(0), "NFTMembership address is invalid"); + assertTrue(participationToken != address(0), "ParticipationToken address is invalid"); + assertTrue(treasury != address(0), "Treasury address is invalid"); + assertTrue(participationVoting != address(0), "ParticipationVoting address is invalid"); + assertTrue(taskManager != address(0), "TaskManager address is invalid"); + assertTrue(quickJoin != address(0), "QuickJoin address is invalid"); + assertTrue(directDemocracyVoting != address(0), "DirectDemocracyVoting address is invalid"); + assertTrue(electionContract == address(0), "ElectionContract should not be deployed"); + assertTrue(educationHub != address(0), "EducationHub address is invalid"); + } else if ( + keccak256(abi.encodePacked(deploymentType)) + == keccak256(abi.encodePacked("ParticipationVotingWithElection")) + ) { + assertTrue(nftMembership != address(0), "NFTMembership address is invalid"); + assertTrue(participationToken != address(0), "ParticipationToken address is invalid"); + assertTrue(treasury != address(0), "Treasury address is invalid"); + assertTrue(participationVoting != address(0), "ParticipationVoting address is invalid"); + assertTrue(taskManager != address(0), "TaskManager address is invalid"); + assertTrue(quickJoin != address(0), "QuickJoin address is invalid"); + assertTrue(directDemocracyVoting != address(0), "DirectDemocracyVoting address is invalid"); + assertTrue(electionContract != address(0), "ElectionContract address is invalid"); + assertTrue(educationHub == address(0), "EducationHub should not be deployed"); } else { revert("Invalid deployment type"); } diff --git a/test/integration/hybridOrg.t.sol b/test/integration/hybridOrg.t.sol new file mode 100644 index 0000000..56c06aa --- /dev/null +++ b/test/integration/hybridOrg.t.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../../src/DirectDemocracyVotingFactory.sol"; +import "../../src/DirectDemocracyTokenFactory.sol"; +import "../../src/HybridVotingFactory.sol"; +import "../../src/ParticipationTokenFactory.sol"; +import "../../src/ParticipationVotingFactory.sol"; +import "../../src/TreasuryFactory.sol"; +import "../../src/MembershipNFTFactory.sol"; +import "../../src/RegistryFactory.sol"; +import "../../src/TaskManagerFactory.sol"; +import "../../src/QuickJoinFactory.sol"; +import "../../src/MasterDeployFactory.sol"; +import "../../src/UniversalAccountRegistry.sol"; +import "../../src/HybridVoting.sol"; +import "../../src/Registry.sol"; +// import "../../src/ElectionContract.sol"; +import "../../src/MembershipNFT.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {DeployMasterFactory} from "../../script/DeployMasterFactory.s.sol"; +import {DeployDirectDemocracyOrg} from "../../script/DeployDirectDemocracyOrg.s.sol"; +import {DeployParticipationOrg} from "../../script/DeployParticipationOrg.s.sol"; +import {DeployHybridOrg} from "../../script/DeployHybridOrg.s.sol"; + +contract MockParticipationToken2 is IParticipationToken2 { + mapping(address => uint256) public balances; + + function mint(address to, uint256 amount) external override { + balances[to] += amount; + } + + function totalSupply() external view override returns (uint256) { + return 0; + } + + function balanceOf(address account) external view override returns (uint256) { + return balances[account]; + } + + function transfer(address recipient, uint256 amount) external override returns (bool) { + return false; + } + + function allowance(address owner, address spender) external view override returns (uint256) { + return 0; + } + + function approve(address spender, uint256 amount) external override returns (bool) { + return false; + } + + function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) { + return false; + } +} + +contract MockNFTMembership11 is INFTMembership11 { + mapping(address => string) public memberTypes; + mapping(address => bool) public executives; + + function checkMemberTypeByAddress(address user) external view override returns (string memory) { + return memberTypes[user]; + } + + function checkIsExecutive(address user) external view override returns (bool) { + return executives[user]; + } + + function setMemberType(address user, string memory memberType) external { + memberTypes[user] = memberType; + } + + function setExecutive(address user, bool isExec) external { + executives[user] = isExec; + } +} + +interface IElectionContract { + struct Candidate { + address candidateAddress; + string candidateName; + } + + function votingContract() external view returns (address); + function createElection(uint256 _proposalId) external returns (uint256, uint256); + function addCandidate(uint256 proposalId, address _candidateAddress, string memory _candidateName) external; + function getElectionDetails(uint256 electionId) external view returns (bool, uint256, bool); + function getCandidates(uint256 electionId) external view returns (Candidate[] memory); + function concludeElection(uint256 proposalId, uint256 winningOption) external; +} + +interface INFTMembership23 { + function quickJoin() external view returns (address); +} + +contract HybridOrgTest is Test { + MasterFactory masterFactory; + HybridVoting hybridVoting; + address directDemocracyRegistryAddress; + address participationVotingRegistryAddress; + address hybridVotingRegistryAddress; + + function setUp() public { + DeployMasterFactory deployMasterFactory = new DeployMasterFactory(); + address masterFactoryAddress = deployMasterFactory.run(); + masterFactory = MasterFactory(masterFactoryAddress); + } + + /** + * @dev test Hybrid Organization with 2 test configurations + * With the Election Hub enabled and disabled. This feature controls voting processes, election events, and governance mechanisms. + * With the Education Hub enabled and disabled. This module is responsible for guiding users through the learning curve of DAO operations and governance. + */ + + /// @dev test with Election Hub + function testHybridElectionHub() public { + //Deploy HybridOrg with Election Hub + DeployHybridOrg deployHybridOrg = new DeployHybridOrg(); + hybridVotingRegistryAddress = deployHybridOrg.run(address(masterFactory), true, false); + Registry registry = Registry(hybridVotingRegistryAddress); + address electionContractAddress = registry.getContractAddress("ElectionContract"); + + address votingContractAddress = IElectionContract(electionContractAddress).votingContract(); + address nftMembership = registry.getContractAddress("NFTMembership"); + + //Election Creation + vm.prank(votingContractAddress); + (uint256 electionId, uint256 proposalId) = IElectionContract(electionContractAddress).createElection(1); + + assertEq(electionId, 0); + assertEq(proposalId, 1); + + address candidate1 = address(0xDEAD); + address candidate2 = address(0xBEEF); + address candidate3 = address(0xFEED); + console.log("Test Voting Contract Address"); + console.logAddress(votingContractAddress); + // Add Candidates + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate1, "Candidate 1"); + + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate2, "Candidate 2"); + + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate3, "Candidate 3"); + + // Conclude Election + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).concludeElection(1, 2); + + // Verify Results + (bool isActive, uint256 winningCandidateIndex, bool hasValidWinner) = + IElectionContract(electionContractAddress).getElectionDetails(electionId); + assertFalse(isActive); + assertTrue(hasValidWinner); + assertEq(winningCandidateIndex, 2); + + // Verify Candidates + IElectionContract.Candidate[] memory candidates = + IElectionContract(electionContractAddress).getCandidates(electionId); + assertEq(candidates.length, 3); + assertEq(candidates[0].candidateAddress, candidate1); + assertEq(candidates[1].candidateAddress, candidate2); + assertEq(candidates[2].candidateAddress, candidate3); + + // Check that the NFT was minted for the winning candidate + assertEq(INFTMembership11(nftMembership).checkMemberTypeByAddress(candidate3), "Executive"); + } + + /// @dev test with Eduction Hub + function testHybridEducationHub() public { + //Deploy HybridOrg with Education Hub + DeployHybridOrg deployHybridOrg = new DeployHybridOrg(); + hybridVotingRegistryAddress = deployHybridOrg.run(address(masterFactory), false, true); + + Registry registry = Registry(hybridVotingRegistryAddress); + address educationHubAddress = registry.getContractAddress("EducationHub"); + + address nftMembership = registry.getContractAddress("NFTMembership"); + address executive = address(deployHybridOrg); + + console.log("executive address"); + console.logAddress(executive); + // test create module + vm.prank(executive); + EducationHub(educationHubAddress).createModule("Intro to DAO", "ipfsHash1", 100, 1); + + (uint256 id, string memory name, string memory ipfsHash, bool exists, uint256 payout, uint8 correctAnswer) = + EducationHub(educationHubAddress).modules(0); + assertEq(id, 0); + assertEq(name, "Intro to DAO"); + assertEq(ipfsHash, "ipfsHash1"); + assertTrue(exists); + assertEq(payout, 100); + assertEq(correctAnswer, 1); + + //test complete module + address member = address(deployHybridOrg); + + // Complete the module as a member + vm.prank(member); + EducationHub(educationHubAddress).completeModule(0, 1); + + // Attempt to complete the module again + vm.prank(member); + vm.expectRevert("Module already completed"); + EducationHub(educationHubAddress).completeModule(0, 1); + + // Remove the module as an executive + vm.prank(executive); + EducationHub(educationHubAddress).removeModule(0); + } +} diff --git a/test/integration/participationOrg.t.sol b/test/integration/participationOrg.t.sol new file mode 100644 index 0000000..67b0aa3 --- /dev/null +++ b/test/integration/participationOrg.t.sol @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../../src/DirectDemocracyVotingFactory.sol"; +import "../../src/DirectDemocracyTokenFactory.sol"; +import "../../src/ParticipationVotingFactory.sol"; +import "../../src/ParticipationTokenFactory.sol"; +import "../../src/ParticipationVotingFactory.sol"; +import "../../src/TreasuryFactory.sol"; +import "../../src/MembershipNFTFactory.sol"; +import "../../src/RegistryFactory.sol"; +import "../../src/TaskManagerFactory.sol"; +import "../../src/QuickJoinFactory.sol"; +import "../../src/MasterDeployFactory.sol"; +import "../../src/UniversalAccountRegistry.sol"; +import "../../src/ParticipationVoting.sol"; +import "../../src/Registry.sol"; +import "../../src/ElectionContract.sol"; +import "../../src/MembershipNFT.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {DeployMasterFactory} from "../../script/DeployMasterFactory.s.sol"; +import {DeployDirectDemocracyOrg} from "../../script/DeployDirectDemocracyOrg.s.sol"; +import {DeployParticipationOrg} from "../../script/DeployParticipationOrg.s.sol"; +import {DeployParticipationOrg} from "../../script/DeployParticipationOrg.s.sol"; + +contract MockParticipationToken2 is IParticipationToken2 { + mapping(address => uint256) public balances; + + function mint(address to, uint256 amount) external override { + balances[to] += amount; + } + + function totalSupply() external view override returns (uint256) { + return 0; + } + + function balanceOf(address account) external view override returns (uint256) { + return balances[account]; + } + + function transfer(address recipient, uint256 amount) external override returns (bool) { + return false; + } + + function allowance(address owner, address spender) external view override returns (uint256) { + return 0; + } + + function approve(address spender, uint256 amount) external override returns (bool) { + return false; + } + + function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) { + return false; + } +} + +contract MockNFTMembership11 is INFTMembership11 { + mapping(address => string) public memberTypes; + mapping(address => bool) public executives; + + function checkMemberTypeByAddress(address user) external view override returns (string memory) { + return memberTypes[user]; + } + + function checkIsExecutive(address user) external view override returns (bool) { + return executives[user]; + } + + function setMemberType(address user, string memory memberType) external { + memberTypes[user] = memberType; + } + + function setExecutive(address user, bool isExec) external { + executives[user] = isExec; + } +} + +interface IElectionContract { + struct Candidate { + address candidateAddress; + string candidateName; + } + + function votingContract() external view returns (address); + function createElection(uint256 _proposalId) external returns (uint256, uint256); + function addCandidate(uint256 proposalId, address _candidateAddress, string memory _candidateName) external; + function getElectionDetails(uint256 electionId) external view returns (bool, uint256, bool); + function getCandidates(uint256 electionId) external view returns (Candidate[] memory); + function concludeElection(uint256 proposalId, uint256 winningOption) external; +} + +contract ParticipationOrgTest is Test { + MasterFactory masterFactory; + ParticipationVoting participationVoting; + address participationVotingRegistryAddress; + + function setUp() public { + DeployMasterFactory deployMasterFactory = new DeployMasterFactory(); + address masterFactoryAddress = deployMasterFactory.run(); + masterFactory = MasterFactory(masterFactoryAddress); + } + + /** + * @dev test Participation Organization with 2 test configurations + * With the Election Hub enabled and disabled. This feature controls voting processes, election events, and governance mechanisms. + * With the Education Hub enabled and disabled. This module is responsible for guiding users through the learning curve of DAO operations and governance. + */ + + /// @dev test with Election Hub + function testParticipationElectionHub() public { + //Deploy ParticipationOrg with Election Hub + DeployParticipationOrg deployParticipationOrg = new DeployParticipationOrg(); + participationVotingRegistryAddress = deployParticipationOrg.run(address(masterFactory), true, false); + Registry registry = Registry(participationVotingRegistryAddress); + address electionContractAddress = registry.getContractAddress("ElectionContract"); + + address votingContractAddress = IElectionContract(electionContractAddress).votingContract(); + address nftMembership = registry.getContractAddress("NFTMembership"); + + //Election Creation + vm.prank(votingContractAddress); + (uint256 electionId, uint256 proposalId) = IElectionContract(electionContractAddress).createElection(1); + + assertEq(electionId, 0); + assertEq(proposalId, 1); + + address candidate1 = address(0xDEAD); + address candidate2 = address(0xBEEF); + address candidate3 = address(0xFEED); + // Add Candidates + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate1, "Candidate 1"); + + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate2, "Candidate 2"); + + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate3, "Candidate 3"); + + // Conclude Election + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).concludeElection(1, 2); + + // Verify Results + (bool isActive, uint256 winningCandidateIndex, bool hasValidWinner) = + IElectionContract(electionContractAddress).getElectionDetails(electionId); + assertFalse(isActive); + assertTrue(hasValidWinner); + assertEq(winningCandidateIndex, 2); + + // Verify Candidates + IElectionContract.Candidate[] memory candidates = + IElectionContract(electionContractAddress).getCandidates(electionId); + assertEq(candidates.length, 3); + assertEq(candidates[0].candidateAddress, candidate1); + assertEq(candidates[1].candidateAddress, candidate2); + assertEq(candidates[2].candidateAddress, candidate3); + + // Check that the NFT was minted for the winning candidate + assertEq(INFTMembership11(nftMembership).checkMemberTypeByAddress(candidate3), "Executive"); + } + + /// @dev test with Eduction Hub + function testParticipationEducationHub() public { + //Deploy ParticipationOrg with Education Hub + DeployParticipationOrg deployPariticipationOrg = new DeployParticipationOrg(); + participationVotingRegistryAddress = deployPariticipationOrg.run(address(masterFactory), false, true); + + Registry registry = Registry(participationVotingRegistryAddress); + address educationHubAddress = registry.getContractAddress("EducationHub"); + + address nftMembership = registry.getContractAddress("NFTMembership"); + address executive = address(deployPariticipationOrg); + + // test create module + vm.prank(executive); + EducationHub(educationHubAddress).createModule("Intro to DAO", "ipfsHash1", 100, 1); + + (uint256 id, string memory name, string memory ipfsHash, bool exists, uint256 payout, uint8 correctAnswer) = + EducationHub(educationHubAddress).modules(0); + assertEq(id, 0); + assertEq(name, "Intro to DAO"); + assertEq(ipfsHash, "ipfsHash1"); + assertTrue(exists); + assertEq(payout, 100); + assertEq(correctAnswer, 1); + + //test complete module + address member = address(deployPariticipationOrg); + + // Complete the module as a member + vm.prank(member); + EducationHub(educationHubAddress).completeModule(0, 1); + + // Attempt to complete the module again + vm.prank(member); + vm.expectRevert("Module already completed"); + EducationHub(educationHubAddress).completeModule(0, 1); + + // Remove the module as an executive + vm.prank(executive); + EducationHub(educationHubAddress).removeModule(0); + } +} diff --git a/test/integration/standardDecentralizationOrg.t.sol b/test/integration/standardDecentralizationOrg.t.sol new file mode 100644 index 0000000..656a764 --- /dev/null +++ b/test/integration/standardDecentralizationOrg.t.sol @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import "../../src/DirectDemocracyVotingFactory.sol"; +import "../../src/DirectDemocracyTokenFactory.sol"; +import "../../src/DirectDemocracyVotingFactory.sol"; +import "../../src/DirectDemocracyTokenFactory.sol"; +import "../../src/DirectDemocracyVotingFactory.sol"; +import "../../src/TreasuryFactory.sol"; +import "../../src/MembershipNFTFactory.sol"; +import "../../src/RegistryFactory.sol"; +import "../../src/TaskManagerFactory.sol"; +import "../../src/QuickJoinFactory.sol"; +import "../../src/MasterDeployFactory.sol"; +import "../../src/UniversalAccountRegistry.sol"; +import "../../src/DirectDemocracyVoting.sol"; +import "../../src/Registry.sol"; +import "../../src/ElectionContract.sol"; +import "../../src/MembershipNFT.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {DeployMasterFactory} from "../../script/DeployMasterFactory.s.sol"; +import {DeployDirectDemocracyOrg} from "../../script/DeployDirectDemocracyOrg.s.sol"; +import {DeployParticipationOrg} from "../../script/DeployParticipationOrg.s.sol"; + +contract MockParticipationToken2 is IParticipationToken2 { + mapping(address => uint256) public balances; + + function mint(address to, uint256 amount) external override { + balances[to] += amount; + } + + function totalSupply() external view override returns (uint256) { + return 0; + } + + function balanceOf(address account) external view override returns (uint256) { + return balances[account]; + } + + function transfer(address recipient, uint256 amount) external override returns (bool) { + return false; + } + + function allowance(address owner, address spender) external view override returns (uint256) { + return 0; + } + + function approve(address spender, uint256 amount) external override returns (bool) { + return false; + } + + function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) { + return false; + } +} + +contract MockNFTMembership11 is INFTMembership11 { + mapping(address => string) public memberTypes; + mapping(address => bool) public executives; + + function checkMemberTypeByAddress(address user) external view override returns (string memory) { + return memberTypes[user]; + } + + function checkIsExecutive(address user) external view override returns (bool) { + return executives[user]; + } + + function setMemberType(address user, string memory memberType) external { + memberTypes[user] = memberType; + } + + function setExecutive(address user, bool isExec) external { + executives[user] = isExec; + } +} + +interface IElectionContract { + struct Candidate { + address candidateAddress; + string candidateName; + } + + function votingContract() external view returns (address); + function createElection(uint256 _proposalId) external returns (uint256, uint256); + function addCandidate(uint256 proposalId, address _candidateAddress, string memory _candidateName) external; + function getElectionDetails(uint256 electionId) external view returns (bool, uint256, bool); + function getCandidates(uint256 electionId) external view returns (Candidate[] memory); + function concludeElection(uint256 proposalId, uint256 winningOption) external; +} + +contract DirectDemocracyOrgTest is Test { + MasterFactory masterFactory; + DirectDemocracyVoting directDemocracyVoting; + address directDemocracyVotingRegistryAddress; + + function setUp() public { + DeployMasterFactory deployMasterFactory = new DeployMasterFactory(); + address masterFactoryAddress = deployMasterFactory.run(); + masterFactory = MasterFactory(masterFactoryAddress); + } + + /** + * @dev test DirectDemocracy Organization with 2 test configurations + * With the Election Hub enabled and disabled. This feature controls voting processes, election events, and governance mechanisms. + * With the Education Hub enabled and disabled. This module is responsible for guiding users through the learning curve of DAO operations and governance. + */ + + /// @dev test with Election Hub + function testDirectDemocracyElectionHub() public { + //Deploy DirectDemocracyOrg with Election Hub + DeployDirectDemocracyOrg deployDirectDemocracyOrg = new DeployDirectDemocracyOrg(); + directDemocracyVotingRegistryAddress = deployDirectDemocracyOrg.run(address(masterFactory), true, false); + Registry registry = Registry(directDemocracyVotingRegistryAddress); + address electionContractAddress = registry.getContractAddress("ElectionContract"); + + address votingContractAddress = IElectionContract(electionContractAddress).votingContract(); + address nftMembership = registry.getContractAddress("NFTMembership"); + + //Election Creation + vm.prank(votingContractAddress); + (uint256 electionId, uint256 proposalId) = IElectionContract(electionContractAddress).createElection(1); + + assertEq(electionId, 0); + assertEq(proposalId, 1); + + address candidate1 = address(0xDEAD); + address candidate2 = address(0xBEEF); + address candidate3 = address(0xFEED); + // Add Candidates + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate1, "Candidate 1"); + + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate2, "Candidate 2"); + + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).addCandidate(1, candidate3, "Candidate 3"); + + // Conclude Election + vm.prank(votingContractAddress); + IElectionContract(electionContractAddress).concludeElection(1, 2); + + // Verify Results + (bool isActive, uint256 winningCandidateIndex, bool hasValidWinner) = + IElectionContract(electionContractAddress).getElectionDetails(electionId); + assertFalse(isActive); + assertTrue(hasValidWinner); + assertEq(winningCandidateIndex, 2); + + // Verify Candidates + IElectionContract.Candidate[] memory candidates = + IElectionContract(electionContractAddress).getCandidates(electionId); + assertEq(candidates.length, 3); + assertEq(candidates[0].candidateAddress, candidate1); + assertEq(candidates[1].candidateAddress, candidate2); + assertEq(candidates[2].candidateAddress, candidate3); + + // Check that the NFT was minted for the winning candidate + assertEq(INFTMembership11(nftMembership).checkMemberTypeByAddress(candidate3), "Executive"); + } + + /// @dev test with Eduction Hub + function testDirectDemocracyEducationHub() public { + //Deploy DirectDemocracyOrg with Education Hub + DeployDirectDemocracyOrg deployDirectDemocracyOrg = new DeployDirectDemocracyOrg(); + directDemocracyVotingRegistryAddress = deployDirectDemocracyOrg.run(address(masterFactory), false, true); + Registry registry = Registry(directDemocracyVotingRegistryAddress); + address educationHubAddress = registry.getContractAddress("EducationHub"); + + address nftMembership = registry.getContractAddress("NFTMembership"); + address executive = address(deployDirectDemocracyOrg); + + // test create module + vm.prank(executive); + EducationHub(educationHubAddress).createModule("Intro to DAO", "ipfsHash1", 100, 1); + + (uint256 id, string memory name, string memory ipfsHash, bool exists, uint256 payout, uint8 correctAnswer) = + EducationHub(educationHubAddress).modules(0); + assertEq(id, 0); + assertEq(name, "Intro to DAO"); + assertEq(ipfsHash, "ipfsHash1"); + assertTrue(exists); + assertEq(payout, 100); + assertEq(correctAnswer, 1); + + //test complete module + address member = address(deployDirectDemocracyOrg); + + // Complete the module as a member + vm.prank(member); + EducationHub(educationHubAddress).completeModule(0, 1); + + // Attempt to complete the module again + vm.prank(member); + vm.expectRevert("Module already completed"); + EducationHub(educationHubAddress).completeModule(0, 1); + + // Remove the module as an executive + vm.prank(executive); + EducationHub(educationHubAddress).removeModule(0); + } +}