diff --git a/test/governance/GovernorAlphaZama.test.ts b/test/governance/GovernorAlphaZama.test.ts index 25ad948..b28a269 100644 --- a/test/governance/GovernorAlphaZama.test.ts +++ b/test/governance/GovernorAlphaZama.test.ts @@ -320,8 +320,8 @@ describe("GovernorAlphaZama", function () { const signatures = ["getBalanceOf(address)"]; const calldatas = [ethers.AbiCoder.defaultAbiCoder().encode(["address"], [this.signers.bob.address])]; const description = "description"; - const transferAmountFor = parseUnits(String(200_000), 6); - const transferAmountAgainst = parseUnits(String(200_000), 6) + BigInt(1); + const transferAmountFor = parseUnits(String(500_000), 6); + const transferAmountAgainst = parseUnits(String(500_000), 6) + BigInt(1); // Bob and Carol receive 200k tokens and delegate to themselves. await transferTokensAndDelegate( @@ -428,12 +428,6 @@ describe("GovernorAlphaZama", function () { await expect( timelockFactory.connect(this.signers.alice).deploy(this.signers.alice.address, 60 * 60 * 24 * 31), ).to.be.revertedWith("Timelock::setDelay: Delay must not exceed maximum delay."); // 31 days > 30 days - } else { - // fhevm-mode - await expect(timelockFactory.connect(this.signers.alice).deploy(this.signers.alice.address, 60 * 60 * 24 * 1)).to - .throw; - await expect(timelockFactory.connect(this.signers.alice).deploy(this.signers.alice.address, 60 * 60 * 24 * 31)).to - .throw; } }); @@ -626,7 +620,7 @@ describe("GovernorAlphaZama", function () { const signatures = ["getBalanceOf(address)"]; const calldatas = [ethers.AbiCoder.defaultAbiCoder().encode(["address"], [this.signers.bob.address])]; const description = "description"; - const transferAmount = parseUnits(String(400_000), 6); + const transferAmount = await this.governor.QUORUM_VOTES(); // Bob receives 400k tokens and delegates to himself. await transferTokensAndDelegate( @@ -681,13 +675,241 @@ describe("GovernorAlphaZama", function () { await expect(this.governor.queue(proposalId)).to.be.revertedWithCustomError(this.governor, "ProposalStateInvalid"); }); + it("Cannot cancel if state is Rejected/Executed/Canceled/Defeated", async function () { + let transferAmount = (await this.governor.PROPOSAL_THRESHOLD()) - BigInt(1); + const targets = [this.signers.bob.address]; + const values = ["0"]; + const signatures = ["getBalanceOf(address)"]; + const calldatas = [ethers.AbiCoder.defaultAbiCoder().encode(["address"], [this.signers.bob.address])]; + const description = "description"; + + // Cannot cancel if Rejected + await transferTokensAndDelegate( + this.signers, + this.instances, + transferAmount, + "bob", + "bob", + this.comp, + this.compAddress, + ); + + let tx = await this.governor.connect(this.signers.bob).propose(targets, values, signatures, calldatas, description); + await tx.wait(); + await awaitAllDecryptionResults(); + + let proposalId = await this.governor.latestProposalIds(this.signers.bob.address); + + await expect(this.governor.connect(this.signers.bob).cancel(proposalId)).to.be.revertedWithCustomError( + this.governor, + "ProposalStateInvalid", + ); + + // Cannot cancel if Defeated + transferAmount = (await this.governor.QUORUM_VOTES()) - BigInt(1); + + await transferTokensAndDelegate( + this.signers, + this.instances, + transferAmount, + "carol", + "carol", + this.comp, + this.compAddress, + ); + + tx = await this.governor.connect(this.signers.carol).propose(targets, values, signatures, calldatas, description); + await tx.wait(); + await awaitAllDecryptionResults(); + + proposalId = await this.governor.latestProposalIds(this.signers.carol.address); + + let input = this.instances.carol.createEncryptedInput(this.governorAddress, this.signers.carol.address); + input.addBool(true); + let encryptedVote = await input.encrypt(); + tx = await this.governor + .connect(this.signers.carol) + ["castVote(uint256,bytes32,bytes)"](proposalId, encryptedVote.handles[0], encryptedVote.inputProof); + await tx.wait(); + + // Mine blocks + await mineNBlocks(4); + + // REQUEST DECRYPTION + tx = await this.governor.requestVoteDecryption(proposalId); + await tx.wait(); + await awaitAllDecryptionResults(); + await expect(this.governor.connect(this.signers.carol).cancel(proposalId)).to.be.revertedWithCustomError( + this.governor, + "ProposalStateInvalid", + ); + + // Cannot cancel if Executed + transferAmount = await this.governor.QUORUM_VOTES(); + + await transferTokensAndDelegate( + this.signers, + this.instances, + transferAmount, + "dave", + "dave", + this.comp, + this.compAddress, + ); + + tx = await this.governor.connect(this.signers.dave).propose(targets, values, signatures, calldatas, description); + await tx.wait(); + await awaitAllDecryptionResults(); + + proposalId = await this.governor.latestProposalIds(this.signers.dave.address); + + input = this.instances.dave.createEncryptedInput(this.governorAddress, this.signers.dave.address); + input.addBool(true); + encryptedVote = await input.encrypt(); + tx = await this.governor + .connect(this.signers.dave) + ["castVote(uint256,bytes32,bytes)"](proposalId, encryptedVote.handles[0], encryptedVote.inputProof); + await tx.wait(); + + // Mine blocks + await mineNBlocks(4); + + // REQUEST DECRYPTION + tx = await this.governor.requestVoteDecryption(proposalId); + await tx.wait(); + await awaitAllDecryptionResults(); + + tx = await this.governor.queue(proposalId); + await tx.wait(); + + const eta = (await this.governor.getProposalInfo(proposalId)).eta; + + // EXECUTE + await ethers.provider.send("evm_setNextBlockTimestamp", [eta.toString()]); + tx = await this.governor.execute(proposalId); + await tx.wait(); + + await expect(this.governor.connect(this.signers.dave).cancel(proposalId)).to.be.revertedWithCustomError( + this.governor, + "ProposalStateInvalid", + ); + }); + + it("Cancel function clears the timelock if the proposal is queued", async function () { + //—> Can cancel by proposer/owner + }); + + it("Cannot propose if not the correct state", async function () { + // + }); + + it("Cannot request vote decryption if state is not Active or if endBlock >= block.number", async function () { + // + }); + + it("Cannot cast a vote if state is not Active or if endBlock > block.number ", async function () { + const targets = [this.signers.bob.address]; + const values = ["0"]; + const signatures = ["getBalanceOf(address)"]; + const calldatas = [ethers.AbiCoder.defaultAbiCoder().encode(["address"], [this.signers.bob.address])]; + const description = "description"; + const transferAmount = await this.governor.QUORUM_VOTES(); + + await transferTokensAndDelegate( + this.signers, + this.instances, + transferAmount, + "bob", + "bob", + this.comp, + this.compAddress, + ); + + let tx = await this.governor.connect(this.signers.bob).propose(targets, values, signatures, calldatas, description); + const proposalId = await this.governor.latestProposalIds(this.signers.bob.address); + + let input = this.instances.bob.createEncryptedInput(this.governorAddress, this.signers.bob.address); + input.addBool(true); + let encryptedVote = await input.encrypt(); + + await expect( + this.governor + .connect(this.signers.bob) + ["castVote(uint256,bytes32,bytes)"](proposalId, encryptedVote.handles[0], encryptedVote.inputProof), + ).to.be.revertedWithCustomError(this.governor, "ProposalStateInvalid"); + + tx = await this.governor.connect(this.signers.bob).cancel(proposalId); + await tx.wait(); + + tx = await this.governor.connect(this.signers.bob).propose(targets, values, signatures, calldatas, description); + await tx.wait(); + await awaitAllDecryptionResults(); + + const newProposalId = await this.governor.latestProposalIds(this.signers.bob.address); + const proposalInfo = await this.governor.getProposalInfo(newProposalId); + expect(proposalInfo.state).to.equal(3); + + // Mine blocks until it is too late to cast vote + await mineNBlocks(5); + + await expect( + this.governor + .connect(this.signers.bob) + ["castVote(uint256,bytes32,bytes)"](newProposalId, encryptedVote.handles[0], encryptedVote.inputProof), + ).to.be.revertedWithCustomError(this.governor, "ProposalStateNotActive"); + }); + + it("Cannot cast a vote twice", async function () { + const targets = [this.signers.bob.address]; + const values = ["0"]; + const signatures = ["getBalanceOf(address)"]; + const calldatas = [ethers.AbiCoder.defaultAbiCoder().encode(["address"], [this.signers.bob.address])]; + const description = "description"; + const transferAmount = await this.governor.QUORUM_VOTES(); + + // Bob receives 400k tokens and delegates to himself. + await transferTokensAndDelegate( + this.signers, + this.instances, + transferAmount, + "bob", + "bob", + this.comp, + this.compAddress, + ); + + // INITIATE A PROPOSAL + let tx = await this.governor.connect(this.signers.bob).propose(targets, values, signatures, calldatas, description); + await tx.wait(); + + // DECRYPTION FOR THE TOKEN THRESHOLD + await awaitAllDecryptionResults(); + const proposalId = await this.governor.latestProposalIds(this.signers.bob.address); + + // VOTE + // Bob casts a vote + let input = this.instances.bob.createEncryptedInput(this.governorAddress, this.signers.bob.address); + input.addBool(true); + let encryptedVote = await input.encrypt(); + tx = await this.governor + .connect(this.signers.bob) + ["castVote(uint256,bytes32,bytes)"](proposalId, encryptedVote.handles[0], encryptedVote.inputProof); + await tx.wait(); + + await expect( + this.governor + .connect(this.signers.bob) + ["castVote(uint256,bytes32,bytes)"](proposalId, encryptedVote.handles[0], encryptedVote.inputProof), + ).to.be.revertedWithCustomError(this.governor, "VoterHasAlreadyVoted"); + }); + it("proposal expires after grace period", async function () { const targets = [this.signers.bob.address]; const values = ["0"]; const signatures = ["getBalanceOf(address)"]; const calldatas = [ethers.AbiCoder.defaultAbiCoder().encode(["address"], [this.signers.bob.address])]; const description = "description"; - const transferAmount = parseUnits(String(400_000), 6); + const transferAmount = await this.governor.QUORUM_VOTES(); // Bob receives 400k tokens and delegates to himself. await transferTokensAndDelegate( @@ -748,3 +970,6 @@ describe("GovernorAlphaZama", function () { expect(proposalInfo.state).to.equal(9); }); }); +function fulfillAllPastRequestsIds(arg0: boolean) { + throw new Error("Function not implemented."); +}