Skip to content

Commit

Permalink
add logic to min proposal level
Browse files Browse the repository at this point in the history
  • Loading branch information
andresaiello committed Oct 30, 2024
1 parent 3bb698d commit 07b8e00
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 7 deletions.
32 changes: 28 additions & 4 deletions packages/zevm-app-contracts/contracts/xp-nft/ZetaXPGov.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ contract ZetaXPGov is Governor, GovernorSettings, GovernorCountingSimple, Govern
bytes32 public tagValidToVote;
ZetaXP_V2 public xpNFT;
uint256 public quorumPercentage; // New state to store the quorum percentage
uint256 public minLevelToPropose; // New state to store the minimum level required to propose

constructor(
ZetaXP_V2 _xpNFT,
Expand All @@ -34,14 +35,26 @@ contract ZetaXPGov is Governor, GovernorSettings, GovernorCountingSimple, Govern
tagValidToVote = _tag;
}

function setQuorumPercentage(uint256 _quorumPercentage) external onlyGovernance {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

quorumPercentage = _quorumPercentage;
}

function setMinLevelToPropose(uint256 _minLevelToPropose) external onlyGovernance {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

minLevelToPropose = _minLevelToPropose;
}

function _getLevel(address account) internal view returns (uint256) {
uint256 tokenId = xpNFT.tokenByUserTag(account, tagValidToVote);
return xpNFT.getLevel(tokenId);
}

// Override the _getVotes function to apply custom weight based on NFT levels
function _getVotes(
address account,
uint256 blockNumber,
bytes memory params
) internal view override returns (uint256) {
uint256 tokenId = xpNFT.tokenByUserTag(account, tagValidToVote);
uint256 level = xpNFT.getLevel(tokenId);
uint256 level = _getLevel(account);

// Assign voting weight based on NFT level
if (level == 1) {
Expand Down Expand Up @@ -126,10 +139,21 @@ contract ZetaXPGov is Governor, GovernorSettings, GovernorCountingSimple, Govern
string memory reason,
bytes memory params
) internal override returns (uint256) {
uint256 tokenId = xpNFT.tokenByUserTag(account, tagValidToVote);
uint256 level = xpNFT.getLevel(tokenId);
uint256 level = _getLevel(account);
require(level > 0, "ZetaXPGov: invalid NFT level");

return super._castVote(proposalId, account, support, reason, params);
}

function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override(Governor, IGovernor) returns (uint256) {
uint256 level = _getLevel(msg.sender);
require(level >= minLevelToPropose, "ZetaXPGov: insufficient level to propose");

return super.propose(targets, values, calldatas, description);
}
}
4 changes: 2 additions & 2 deletions packages/zevm-app-contracts/data/addresses.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"ZetaXP": "0x5c25b6f4D2b7a550a80561d3Bf274C953aC8be7d",
"InstantRewards": "0x10DfEd4ba9b8F6a1c998E829FfC0325D533c80E3",
"ProofOfLiveness": "0x981EB6fD19717Faf293Fba0cBD05C6Ac97b8C808",
"TimelockController": "0x75C2C7dD9f8Fe9f7B303570Db164504106D019F5",
"ZetaXPGov": "0xCA95803a99000863D935D3aA86a1a7F190F06D0a"
"TimelockController": "0x44139C2150c11c25f517B8a8F974b59C82aEe709",
"ZetaXPGov": "0x854032d484aE21acC34F36324E55A8080F21Af12"
},
"zeta_mainnet": {
"disperse": "0x23ce409Ea60c3d75827d04D9db3d52F3af62e44d",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getZEVMAppAddress, saveAddress } from "../address.helpers";

const networkName = network.name;

const user = "0x3a600ECC217387e7Cf9F82fc2F3ffFb43F20FF80";
const user = "0x19caCb4c0A7fC25598CC44564ED0eCA01249fc31";
const encodeTag = (tag: string) => ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["string"], [tag]));

// Helper function to set the level of an NFT
Expand Down
78 changes: 78 additions & 0 deletions packages/zevm-app-contracts/test/xp-nft/zeta-xp-gov.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,82 @@ describe("ZetaXPGov", () => {
expect(votes.againstVotes).to.equal(1);
expect(votes.forVotes).to.equal(3);
});

it("Should revert if try to vote with 0 voting power", async () => {
const user1 = addrs[0];

// Create a proposal to vote on
const targets = ["0x0000000000000000000000000000000000000000"];
const values = [0];
const calldatas = ["0x"];
const description = "Proposal #1";

const proposeTx = await zetaGov.connect(signer).propose(targets, values, calldatas, description);
const proposeReceipt = await proposeTx.wait();
const proposalId = proposeReceipt.events?.find((e) => e.event === "ProposalCreated")?.args?.proposalId;

// Increase the time and mine blocks to move to the voting phase
await ethers.provider.send("evm_increaseTime", [7200]); // Fast forward 2 hours to ensure voting delay is over
await ethers.provider.send("evm_mine", []); // Mine the next block

// Both users vote for the proposal using their NFTs
const tx = zetaGov.connect(user1).castVote(proposalId, VoteType.FOR);
await expect(tx).to.be.revertedWith("ZetaXPGov: invalid NFT level");
});

it("Should revert if try to propose and min level is not archive", async () => {
const user1 = addrs[0];
const user2 = addrs[1];
const user3 = addrs[2];

// Mint NFTs to both users
const nftId1 = await mintNFTToUser(user1);
await setLevelToNFT(nftId1, 3);

const nftId2 = await mintNFTToUser(user2);
await setLevelToNFT(nftId2, 2);

const nftId3 = await mintNFTToUser(user3);
await setLevelToNFT(nftId3, 1);

// Create a proposal to vote on
const targets = [zetaGov.address];
const values = [0];
const calldatas = [zetaGov.interface.encodeFunctionData("setMinLevelToPropose", [2])];
const description = "Update min level to propose";

const proposeTx = await zetaGov.connect(signer).propose(targets, values, calldatas, description);
const proposeReceipt = await proposeTx.wait();
const proposalId = proposeReceipt.events?.find((e) => e.event === "ProposalCreated")?.args?.proposalId;

// Increase the time and mine blocks to move to the voting phase
await ethers.provider.send("evm_increaseTime", [7200]); // Fast forward 2 hours to ensure voting delay is over
await ethers.provider.send("evm_mine", []); // Mine the next block

// Both users vote for the proposal using their NFTs
await zetaGov.connect(user1).castVote(proposalId, VoteType.FOR);
await zetaGov.connect(user2).castVote(proposalId, VoteType.ABSTAIN);
await zetaGov.connect(user3).castVote(proposalId, VoteType.AGAINST);

// Optionally, increase the block number to simulate time passing and end the voting period
await ethers.provider.send("evm_increaseTime", [50400]); // Fast forward 1 week to end the voting period
await ethers.provider.send("evm_mine", []); // Mine the next block

// Queue the proposal after voting period is over
const descriptionHash = ethers.utils.id(description);
await zetaGov.connect(signer).queue(targets, values, calldatas, descriptionHash);

// Increase time to meet the timelock delay
await ethers.provider.send("evm_increaseTime", [3600]); // Fast forward 1 hour to meet timelock delay
await ethers.provider.send("evm_mine", []); // Mine the next block

// Execute the proposal after the timelock delay has passed
const executeTx = await zetaGov.connect(signer).execute(targets, values, calldatas, descriptionHash);
await executeTx.wait();

{
const proposeTx = zetaGov.connect(signer).propose(targets, values, calldatas, description);
await expect(proposeTx).to.be.revertedWith("ZetaXPGov: insufficient level to propose");
}
});
});

0 comments on commit 07b8e00

Please sign in to comment.