Governance Freeze PostMortem

Glow Team / July 4, 2025

Governance Freeze PostMortem

An incorrect constructor argument in the Veto Council deployment during the initial Glow V1 guarded-launch caused Governance to reference the wrong contract. When the first proposal to modify council membership executed, the call reverted, blocking and freezing the entire Governance pipeline.

This means that for the rest of Glow V1 there can no longer be on chain GCA Elections, Veto Council Elections, or Grants. Grants will proceed off-chain with support from the ICRG Foundation until the Glow V2 relaunch unfreezes execution and no funds in the protocol are affected. The bug does not affect solar farms, rewards, USDG Redemptions, or any other critical on-chain process.

Code Dive

In Glow V1, the happy path for governance follows a five step process, outlined below:

1. Proposal Creation

• Users call one of the create*Proposal entry points (createGrantsProposal, createGCACouncilElectionOrSlashProposal, etc.).
• Each function:

  1. Calculates the nomination cost (costForNewProposalAndUpdateLastExpiredProposalId).
  2. Burns the caller's nominations via _spendNominations.
  3. Stores a Proposal struct in _proposals and, if its vote count tops the leaderboard, records it in mostPopularProposalOfWeek.

2. Collecting Votes

• Additional nominations can later be attached with useNominationsOnProposal.
• Once the popularity week ends, long-staked GLW holders record ratify/reject votes through ratifyOrReject.
• Votes accumulate in _proposalLongStakerVotes.

3. Eligibility Window

A proposal becomes executable when:
• Its popularity week has finished and
• The four-week ratify/reject & veto window (_NUM_WEEKS_TO_VOTE_ON_MOST_POPULAR_PROPOSAL) has elapsed.

4. Execution Engine (syncProposals)

Anyone can invoke syncProposals() to process the backlog sequentially—oldest first.

function syncProposals() public {
    ...
    for (_nextWeekToExecute; _nextWeekToExecute < currentWeek; ++_nextWeekToExecute) {
        uint256 proposalId = mostPopularProposalOfWeek[_nextWeekToExecute];
        ...
        handleProposalExecution(_nextWeekToExecute, proposalId, proposalType, proposal.data);
    }
}

If any execution reverts, the loop aborts and lastExecutedWeek remains unchanged, blocking every subsequent proposal.

5. Type-specific Dispatch (handleProposalExecution)

handleProposalExecution decodes the payload and forwards the action to the correct subsystem:

if (proposalType == IGovernance.ProposalType.VETO_COUNCIL_ELECTION_OR_SLASH) {
    (address oldMember, address newMember, bool slashOldMember) = abi.decode(data,(address,address,bool));
    success = IVetoCouncil(VETO_COUNCIL).addAndRemoveCouncilMember(oldMember, newMember, slashOldMember);
}

Other branches cover Grants payouts, GCA requirement changes, RFC logging, etc.
Success or failure is stamped via _setProposalStatus, and an event is emitted.


Where It Broke

During week 76, the queued proposal was a VETO_COUNCIL_ELECTION_OR_SLASH.
When syncProposals() hit the dispatch above, the call to VetoCouncil.addAndRemoveCouncilMember() reverted with CallerNotGovernance because the Veto Council contract's immutable governance field had been mis-wired to the GLW token address during deployment:

// DeployGuardedLaunch.s.sol
vetoCouncilContract = new VetoCouncil(address(glow), address(glow), startingVetoCouncilAgents);
//                          ^^^^^^^^^^^^^^^^^^^^^  (should be Governance)

The revert bubbled up, terminated the for loop, and left lastExecutedWeek stuck.
Because syncProposals() insists on synchronous, chronological execution (week == lastExecutedWeek + 1), the entire Governance pipeline froze—no subsequent proposals (including grant payouts) can progress until a redeploy wires the Veto Council to the correct Governance address.

Impact

  • Governance execution frozen. No on-chain grants, parameter changes, or council elections can complete.
  • Grants moved off-chain. The Foundation will aid in an off-chain grant process manually until V2 is operational.
  • No funds at risk. Contract balances and state remain intact; the issue is a logic lock, not a security breach.

Preventing These Issues

Ultimately, the value of any post-mortem lies in the concrete safeguards it inspires. The four mitigations below - type-safe constructors, AI-assisted static analysis, end-to-end deploy-script tests, and dual-review canary deployments - turn the lessons of this incident into hard guardrails baked into our tooling and release flow. Together they make a repeat of the mis-wiring error mechanically unlikely and surface any similar faults long before they can reach mainnet.

  • Strongly-typed constructors in new code. All new contracts receive explicit interface parameters; misuse fails at compile time.
    • Deployment scripts pass interface-typed objects instead of plain addresses, eliminating class-mismatch errors.
  • AI-assisted static analysis. New codebases run an LLM ruleset that flags duplicated or suspicious constructor arguments before merge.
  • Comprehensive deploy-script tests. Each script forks mainnet, executes the deployment end-to-end, and asserts post-deploy invariants, blocking any transaction that does not wire contracts correctly.
  • Mandatory multi-party review and canary execution. Every mainnet deploy requires dual-signature review of calldata and a post-deploy smoke test on a fork, preventing mis-configured contracts from reaching production.

Related articles

What is Glow?

David Vorick / July 4, 2024

5 min read

What is Glow?

An overview of the Glow protocol and its mission to revolutionize the solar energy landscape.

Exploring the GCA Protocol Binary Bug

Simon Boccara / April 10, 2024

5 min read

Exploring the GCA Protocol Binary Bug

We go through the GCA Binary bug that caused weeks [0-8] to be aggregated into a single week. We explore how this affected the rewards for farms and the carbon credits submitted on-chain.

The First Major Refactor of the Glow Audit Standard

David Vorick / March 29, 2024

7 min read

The First Major Refactor of the Glow Audit Standard

Discloses the first cheating incident on the Glow protocol, and explains how the audit standard was refactored to be more effective.

The Glow Impact Platform

David Vorick / January 31, 2024

10 min read

The Glow Impact Platform

Rebuilding climate strategy from first principles.

Guarded Launch: Protecting Glow Users Against Hacks

David Vorick / January 9, 2024

3 min read

Guarded Launch: Protecting Glow Users Against Hacks

Code audits are both expensive and insufficient; using a guarded launch can improve safety while reducing costs and time-to-market.

Why Glow Excites Me

David Vorick / December 19, 2023

5 min read

Why Glow Excites Me

How I found my way to Glow, and why Glow is the path to effective climate action.

Week 40 Bucket Submission Post-Mortem

Simon Boccara / September 10, 2024

5 min read

Week 40 Bucket Submission Post-Mortem

An analysis of the incident during week 40's bucket submission, its impact, and the steps taken to resolve and prevent future occurrences.

Adding USDG Redemptions to the Glow V1 Guard

Simon Boccara / June 25, 2025

5 min read

Adding USDG Redemptions to the Glow V1 Guard

Glow's core contracts went live on Ethereum mainnet on December 18, 2023 under an intentionally conservative guarded launch framework. This post explains how Glow added USDG redemptions to the Glow V1 guard.

Glow Logo

A community working together to build a more
sustainable energy grid.

©2023 GlowGreen. All rights reserved.