WARNING: This smart contract has not undergone a formal security audit. Using this code in production carries significant risks. Always conduct professional security audits before deploying any smart contract to a live environment. The code is presented for educational purposes only.

In our previous articles, we explored the architecture, financial mechanisms, environmental impact tracking, and tranche structuring of the GreenBonds contract. This article focuses on the governance system, which enables bondholders to participate in decision-making.

Governance Framework

The governance system in the contract allows for decentralized decision-making regarding bond parameters, fund allocation, and project direction. This increases transparency and gives bondholders a voice in how their capital is managed.

Governance Parameters

The contract defines key governance parameters:

// Governance parameters
uint256 public quorum;
uint256 public votingPeriod;

These parameters determine:

The minimum percentage of bonds required to participate for a vote to be valid (quorum)The duration of the voting period in seconds

These parameters are initialized during contract deployment:

// Initialize governance parameters
quorum = _totalSupply * 30 / 100; // 30% quorum
votingPeriod = 7 days;

Proposal Structure

Proposals are stored in a dedicated struct:

struct Proposal {
address proposer;
string description;
bytes callData;
address target;
uint256 forVotes;
uint256 againstVotes;
uint256 startTime;
uint256 endTime;
bool executed;
mapping(address => bool) hasVoted;
}

mapping(uint256 => Proposal) public proposals;
uint256 public proposalCount;

Each proposal contains:

The proposer’s addressA description of the proposalThe call data to be executed if the proposal passesThe target contract for the callVote tracking (for votes, against votes)Time parameters (start and end times)Execution statusA mapping to track voter participation

Proposal Creation

Proposals can be created by addresses with the ISSUER_ROLE:

function createProposal(string memory description, address target, bytes memory callData)
external
onlyRole(ISSUER_ROLE)
whenNotPaused
returns (uint256)
{
uint256 proposalId = proposalCount++;
Proposal storage proposal = proposals[proposalId];

proposal.proposer = msg.sender;
proposal.description = description;
proposal.target = target;
proposal.callData = callData;
proposal.startTime = block.timestamp;
proposal.endTime = block.timestamp + votingPeriod;
proposal.executed = false;

emit ProposalCreated(proposalId, msg.sender, description);

return proposalId;
}

This function:

Increments the proposal counterInitializes a new proposal with the provided parametersSets the voting period based on the current governance parametersEmits an event to notify stakeholdersReturns the proposal ID for reference

Voting Mechanism

Bondholders can vote on proposals, with voting power proportional to their bond holdings:

function castVote(uint256 proposalId, bool support) external whenNotPaused {
if (proposalId >= proposalCount) revert ProposalDoesNotExist();

Proposal storage proposal = proposals[proposalId];
if (block.timestamp > proposal.endTime) revert VotingPeriodEnded();
if (proposal.executed) revert ProposalAlreadyExecuted();
if (proposal.hasVoted[msg.sender]) revert AlreadyVoted();

uint256 votingPower = balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();

proposal.hasVoted[msg.sender] = true;

if (support) {
proposal.forVotes += votingPower;
} else {
proposal.againstVotes += votingPower;
}

emit VoteCast(msg.sender, proposalId, support, votingPower);
}

This function:

Validates that the proposal exists and is activeChecks that the voter hasn’t already votedDetermines voting power based on bond holdingsRecords the vote based on support or oppositionEmits an event to track voting activity

Proposal Execution

After the voting period ends, successful proposals can be executed:

function executeProposal(uint256 proposalId) external nonReentrant whenNotPaused {
if (proposalId >= proposalCount) revert ProposalDoesNotExist();

Proposal storage proposal = proposals[proposalId];
if (block.timestamp <= proposal.endTime) revert VotingPeriodEnded();
if (proposal.executed) revert ProposalAlreadyExecuted();

// Check if quorum is met and proposal passed
if (proposal.forVotes + proposal.againstVotes < quorum) revert QuorumNotReached();
if (proposal.forVotes <= proposal.againstVotes) revert ProposalRejected();

proposal.executed = true;

// Execute the proposal
(bool success, ) = proposal.target.call(proposal.callData);
if (!success) revert FailedExecution();

emit ProposalExecuted(proposalId);
}

This function enforces several conditions:

The voting period must have endedThe proposal must not have been executed alreadyThe quorum requirement must be metThe “for” votes must exceed the “against” votes

If these conditions are satisfied, the function:

Marks the proposal as executedCalls the target contract with the specified call dataReverts if the execution failsEmits an event to notify stakeholders

Updating Governance Parameters

The governance parameters can be updated by the admin through a time-locked operation:

function updateGovernanceParams(uint256 newQuorum, uint256 newVotingPeriod)
external
onlyRole(DEFAULT_ADMIN_ROLE)
whenNotPaused
{
if (newQuorum == 0) revert InvalidValue();
if (newVotingPeriod == 0) revert InvalidValue();

bytes32 operationId = keccak256(abi.encodePacked(“updateGovernance”, newQuorum, newVotingPeriod));

if (checkAndScheduleOperation(operationId)) {
uint256 oldQuorum = quorum;
uint256 oldVotingPeriod = votingPeriod;

// Update storage
quorum = newQuorum;
votingPeriod = newVotingPeriod;

emit GovernanceParamsUpdated(oldQuorum, newQuorum, oldVotingPeriod, newVotingPeriod);
emit OperationExecuted(operationId);
}
}

This function:

Validates the new parametersCreates a unique operation IDChecks if the operation is already scheduled, and if not, schedules itIf the timelock has expired, updates the parametersEmits events to track the changes

Timelock Mechanism

The contract includes a timelock mechanism with tracking of executed operations:

mapping(bytes32 => uint256) public operationTimestamps;
uint256 public constant TIMELOCK_PERIOD = 2 days;
mapping(bytes32 => bool) public isOperationExecuted;

The checkAndScheduleOperation function includes protection against re-execution:

function checkAndScheduleOperation(bytes32 operationId) internal returns (bool) {
if (isOperationExecuted[operationId]) {
revert OperationAlreadyExecuted();
}

if (operationTimestamps[operationId] == 0) {
scheduleOperation(operationId);
return false;
}

if (block.timestamp < operationTimestamps[operationId]) {
revert TimelockNotExpired();
}

isOperationExecuted[operationId] = true;
return true;
}

This mechanism:

Checks if an operation has already been executed to prevent re-executionSchedules the operation if it hasn’t been scheduled yetValidates that the timelock period has expiredMarks the operation as executed when it runs

Operations that use the timelock include:

Updating coupon periodIssuer emergency withdrawalsEmergency recoveryUpdating governance parameters

Governance Use Cases

The governance system enables several key use cases:

Parameter Adjustments

Governance proposals can adjust bond parameters such as:

Coupon ratesEarly redemption penaltiesVerification requirements for impact reports

Fund Allocation

Proposals can direct project funds to specific initiatives:

Allocating funds to new green projectsAdjusting the distribution of existing fundsApproving exceptional expenditures

Emergency Actions

In case of market disruption or project issues, proposals can authorize emergency measures:

Pausing coupon paymentsAccelerating or delaying project timelinesImplementing contingency plans

Upgrading the Contract

While the upgrade functionality is restricted to addresses with the UPGRADER_ROLE, governance could be used to authorize who receives this role, creating a more decentralized upgrade process.

Bondholders’ Rights and Responsibilities

The governance system grants bondholders both rights and responsibilities:

Rights

Voting on proposals with weight proportional to bond holdingsProposing changes (if granted the ISSUER_ROLE)Accessing transparent information about votes and execution

Responsibilities

Participating in votes to ensure quorum is reachedReviewing proposals to make informed decisionsMonitoring the execution of approved proposals

Conclusion

The governance system in the GreenBonds contract adds an important layer of decentralization and transparency to the green bond framework. By enabling bondholders to participate in decision-making, it aligns the interests of issuers and investors while providing mechanisms for adapting to changing circumstances.

In the final article of this series, we’ll examine the contract’s security features, fund management, and emergency mechanisms.

Part 5: Governance and On-Chain Decision Making was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.

By

Leave a Reply

Your email address will not be published. Required fields are marked *