Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement the governance smart-contract #193

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions packages/zevm-app-contracts/contracts/xp-nft/ZetaXPGov.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
import "@openzeppelin/contracts/interfaces/IERC6372.sol";

import "./xpNFT_V2.sol";

contract ZetaXPGov is Governor, GovernorSettings, GovernorCountingSimple, GovernorTimelockControl {
bytes32 public tagValidToVote;
ZetaXP_V2 public xpNFT;
Dismissed Show dismissed Hide dismissed
uint256 public quorumPercentage; // New state to store the quorum percentage
Dismissed Show dismissed Hide dismissed
andresaiello marked this conversation as resolved.
Show resolved Hide resolved

constructor(
ZetaXP_V2 _xpNFT,
TimelockController _timelock,
uint256 _quorumPercentage // Set the quorum percentage (e.g., 4%)
)
Governor("ZetaXPGov")
GovernorSettings(7200 /* 1 day */, 50400 /* 1 week */, 0)
GovernorTimelockControl(_timelock)
{
xpNFT = _xpNFT;
quorumPercentage = _quorumPercentage;
}
andresaiello marked this conversation as resolved.
Show resolved Hide resolved

function setTagValidToVote(bytes32 _tag) external onlyGovernance {
Dismissed Show dismissed Hide dismissed
tagValidToVote = _tag;
}

// 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);

andresaiello marked this conversation as resolved.
Show resolved Hide resolved
// Assign voting weight based on NFT level
if (level == 1) {
return 1; // Rosegold
} else if (level == 2) {
return 2; // Black
} else if (level == 3) {
return 3; // Green
} else {
return 0; // Silver cannot vote
}
andresaiello marked this conversation as resolved.
Show resolved Hide resolved
}

// Manually implement the quorum function to define quorum based on the total percentage of votes
function quorum(uint256 blockNumber) public view override returns (uint256) {
uint256 totalSupply = xpNFT.totalSupply(); // Total number of NFTs in circulation
return (totalSupply * quorumPercentage) / 100; // Quorum calculation based on the percentage
}
andresaiello marked this conversation as resolved.
Show resolved Hide resolved

// Override the _execute function to resolve the conflict
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) {
super._execute(proposalId, targets, values, calldatas, descriptionHash);
}

// Override the supportsInterface function to resolve the conflict
function supportsInterface(
bytes4 interfaceId
) public view override(Governor, GovernorTimelockControl) returns (bool) {
return super.supportsInterface(interfaceId);
}

// Implementation of clock and CLOCK_MODE functions to comply with IERC6372
function clock() public view override returns (uint48) {
return uint48(block.timestamp);
}

function CLOCK_MODE() public view override returns (string memory) {
return "mode=timestamp";
}
Dismissed Show dismissed Hide dismissed

// The rest of the functions required to be overridden by Solidity

function votingDelay() public view override(IGovernor, GovernorSettings) returns (uint256) {
return super.votingDelay();
}

function votingPeriod() public view override(IGovernor, GovernorSettings) returns (uint256) {
return super.votingPeriod();
}

function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) {
return super.state(proposalId);
}

function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}

function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
return super._cancel(targets, values, calldatas, descriptionHash);
}

function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
return super._executor();
}
}
6 changes: 3 additions & 3 deletions packages/zevm-app-contracts/contracts/xp-nft/xpNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

contract ZetaXP is ERC721Upgradeable, Ownable2StepUpgradeable, EIP712Upgradeable {
bytes32 private constant MINTORUPDATE_TYPEHASH =
bytes32 internal constant MINTORUPDATE_TYPEHASH =
keccak256("MintOrUpdateNFT(address to,uint256 signatureExpiration,uint256 sigTimestamp,bytes32 tag)");

struct UpdateData {
Expand All @@ -28,7 +28,7 @@
address public signerAddress;

// Counter for the next token ID
uint256 private _currentTokenId;
uint256 internal _currentTokenId;

// Event for New Mint
event NFTMinted(address indexed sender, uint256 indexed tokenId, bytes32 tag);
Expand Down Expand Up @@ -97,25 +97,25 @@
return super.supportsInterface(interfaceId);
}

function _verify(uint256 tokenId, UpdateData memory updateData) private view {
function _verify(uint256 tokenId, UpdateData memory updateData) internal view {
bytes32 structHash = keccak256(
abi.encode(
MINTORUPDATE_TYPEHASH,
updateData.to,
updateData.signatureExpiration,
updateData.sigTimestamp,
updateData.tag
)
);
bytes32 constructedHash = _hashTypedDataV4(structHash);

if (!SignatureChecker.isValidSignatureNow(signerAddress, constructedHash, updateData.signature)) {
revert InvalidSigner();
}

if (block.timestamp > updateData.signatureExpiration) revert SignatureExpired();
if (updateData.sigTimestamp <= lastUpdateTimestampByTokenId[tokenId]) revert OutdatedSignature();
}

Check notice

Code scanning / Slither

Block timestamp Low


function _updateNFT(uint256 tokenId, UpdateData memory updateData) internal {
_verify(tokenId, updateData);
Expand Down
14 changes: 12 additions & 2 deletions packages/zevm-app-contracts/contracts/xp-nft/xpNFT_V2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import "./xpNFT.sol";

contract ZetaXP_V2 is ZetaXP {
bytes32 private constant SETLEVEL_TYPEHASH =
bytes32 internal constant SETLEVEL_TYPEHASH =
keccak256("SetLevel(uint256 tokenId,uint256 signatureExpiration,uint256 sigTimestamp,uint256 level)");

struct SetLevelData {
Expand All @@ -16,25 +16,31 @@
}

mapping(uint256 => uint256) public levelByTokenId;

// Event for Level Set
event LevelSet(address indexed sender, uint256 indexed tokenId, uint256 level);

function version() public pure override returns (string memory) {
return "2.0.0";
}

function _verifySetLevelSignature(SetLevelData memory data) private view {
function _verifyUpdateNFTSignature(uint256 tokenId, UpdateData memory updateData) internal view {
_verify(tokenId, updateData);
}
Dismissed Show dismissed Hide dismissed
andresaiello marked this conversation as resolved.
Show resolved Hide resolved
andresaiello marked this conversation as resolved.
Show resolved Hide resolved

function _verifySetLevelSignature(SetLevelData memory data) internal view {
bytes32 structHash = keccak256(
abi.encode(SETLEVEL_TYPEHASH, data.tokenId, data.signatureExpiration, data.sigTimestamp, data.level)
);
bytes32 constructedHash = _hashTypedDataV4(structHash);

if (!SignatureChecker.isValidSignatureNow(signerAddress, constructedHash, data.signature)) {
revert InvalidSigner();
}

if (block.timestamp > data.signatureExpiration) revert SignatureExpired();
if (data.sigTimestamp <= lastUpdateTimestampByTokenId[data.tokenId]) revert OutdatedSignature();
}

Check notice

Code scanning / Slither

Block timestamp Low


function setLevel(SetLevelData memory data) external {
_verifySetLevelSignature(data);
Expand All @@ -47,4 +53,8 @@
function getLevel(uint256 tokenId) external view returns (uint256) {
return levelByTokenId[tokenId];
}

function totalSupply() external view returns (uint256) {
return _currentTokenId - 1;
}
}
Loading