Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Setting Up On-Chain Governance for Outcome Finalization

A technical tutorial for implementing a secure, on-chain voting system where token holders resolve ambiguous prediction market outcomes and trigger settlements.
Chainscore © 2026
introduction
INTRODUCTION

Setting Up On-Chain Governance for Outcome Finalization

This guide explains how to implement a secure and transparent on-chain governance system to finalize off-chain outcomes, such as votes, predictions, or data attestations.

On-chain governance for outcome finalization is a critical pattern for bridging off-chain events with blockchain state. It involves creating a smart contract system that accepts, validates, and immutably records the results of external processes. Common use cases include finalizing the winner of a prediction market, recording the result of a community vote held on a separate platform, or attesting to the validity of real-world data provided by an oracle. The core challenge is ensuring the process is trust-minimized and resistant to manipulation, often requiring multiple signatures, time delays, or economic slashing mechanisms.

A basic implementation involves a governance contract with a privileged role—like a finalizer—authorized to submit outcomes. A more decentralized approach uses a multi-signature wallet (e.g., Safe) or a DAO structure where a proposal must pass before an outcome is recorded. For highest security, consider a model with a challenge period, where submitted outcomes can be disputed by staking collateral, triggering a verification process. Key contract functions typically include submitOutcome(bytes32 outcomeHash), finalizeOutcome(uint256 proposalId, bytes calldata result), and challengeOutcome(uint256 outcomeId).

When designing the system, you must decide on data representation. Will you store the full result data on-chain, or only a hash? Storing only a bytes32 commitment saves gas but requires users to trust an external data availability layer. Emitting an event with the full data is a common compromise. Furthermore, integrate with existing governance frameworks when possible. For example, you can use OpenZeppelin's Governor contract as the base, where finalizing an outcome is the execution step of a successful proposal. This leverages built-in features like vote tracking, timelocks, and delegation.

Always include clear access controls and emergency functions. Use OpenZeppelin's AccessControl to manage the FINALIZER_ROLE. Implement a timelock contract between the governance vote and execution to allow users to exit positions if they disagree with a pending outcome. For transparency, all parameters—like the finalizer address, challenge period duration, and required stake—should be configurable via governance itself. This creates a self-sustaining system where the rules can evolve without requiring a hard fork or centralized intervention.

Testing is paramount. Write comprehensive unit tests for the main flows: successful finalization, failed finalization due to invalid permissions, and successful challenges. Use forked mainnet tests to simulate interactions with live governance frameworks like Compound or Aave. Finally, consider the user experience for off-chain participants. They need a clear way to query the final, on-chain result. Your contract should expose a view function like getFinalizedOutcome(uint256 eventId) returns (bytes memory) to serve as the single source of truth for all downstream applications.

prerequisites
PREREQUISITES

Setting Up On-Chain Governance for Outcome Finalization

Before implementing an on-chain governance system to finalize outcomes, you need to establish the foundational technical and conceptual framework.

The core prerequisite is a smart contract system that defines the governance process. This includes a contract to manage proposals, a token contract for voting power (like an ERC-20 or ERC-721), and a contract to execute approved actions. You must decide on key parameters: the proposal lifecycle (submission, voting, timelock, execution), quorum requirements, and vote weighting (e.g., token-weighted, quadratic). Frameworks like OpenZeppelin's Governor contracts provide a modular starting point for these components.

Your application must have a clear, objectively verifiable outcome that can be resolved on-chain. This could be the result of an external event (like a sports match via an oracle), a DAO treasury spend proposal, or a protocol parameter change. The data source for finalizing this outcome must be trust-minimized and reliable. For off-chain events, integrate a decentralized oracle network like Chainlink or a custom oracle with a robust security model to feed data into your governance contract.

You will need a front-end interface for users to interact with the governance system. This involves connecting wallets (using libraries like ethers.js or viem), fetching proposal data from the contracts, and submitting transactions for creating proposals, casting votes, and executing results. Consider using a subgraph from The Graph for efficient querying of proposal history and voting states, which is essential for a good user experience.

Security is paramount. Before deployment, conduct thorough testing and audits. Use a development framework like Hardhat or Foundry to write unit and integration tests that simulate the full governance lifecycle, including edge cases like proposal cancellation and vote manipulation. Consider implementing a timelock delay on executed actions, which gives users a window to exit if a malicious proposal passes, adding a critical layer of protection.

Finally, plan for the operational aspects. Determine who can create proposals (any token holder, a multisig council, or delegated delegates). Establish clear documentation for the governance process and the criteria for outcome finalization. For live deployment, you'll need a testnet phase (on networks like Sepolia or Goerli) to validate the system with real users before launching on mainnet.

system-architecture
SYSTEM ARCHITECTURE

Setting Up On-Chain Governance for Outcome Finalization

This guide details the implementation of a modular, upgradeable on-chain governance system for finalizing outcomes, using a proxy pattern and key contracts like a Governor and a Timelock.

On-chain governance for outcome finalization requires a secure, transparent, and upgradeable architecture. The core system typically employs a proxy pattern, separating logic from storage. A TransparentUpgradeableProxy contract holds the state (like proposal data and vote tallies), while a Governor contract contains the executable logic. A TimelockController acts as the executor, introducing a mandatory delay between a proposal's approval and its execution. This separation of powers—proposing, voting, delaying, and executing—is critical for security, preventing rushed or malicious state changes. The Timelock often becomes the owner of other protocol contracts, ensuring only properly ratified governance decisions can modify them.

The primary contract is the Governor, which manages the proposal lifecycle. Key functions include propose(), castVote(), queue(), and execute(). Proposals target the Timelock address with encoded function calls. Voting power is usually derived from an ERC-20 governance token via an integrated voting module, such as OpenZeppelin's GovernorVotes. The governance parameters—like votingDelay, votingPeriod, proposalThreshold, and quorum—are set in the constructor and dictate the proposal timeline and requirements. For example, a common setup might have a 1-block voting delay, a 3-day voting period, and a quorum of 4% of the total token supply.

Integration with a TimelockController is essential for safe execution. After a proposal succeeds, it must be queued to the Timelock, which schedules it for execution after a minimum delay (e.g., 48 hours). This timelock period allows users to review the finalized transaction before it affects the system. The execute function can only be called after this delay expires. The Timelock also enables complex, multi-step operations (like upgrading the Governor itself) to be bundled into a single proposal. All contracts should be thoroughly verified on block explorers like Etherscan, and initial roles (like Timelock admin) should be transferred to the governance system itself to achieve full decentralization.

A practical deployment flow involves several steps. First, deploy the logic contract for your Governor (e.g., GovernorContract.sol). Next, deploy the TimelockController, initializing it with the deployer as the initial admin. Then, deploy the TransparentUpgradeableProxy, pointing it to the Governor logic contract and setting the Timelock as the admin. Finally, initialize the proxy contract with parameters like the governance token address and timelock duration. Use a script for reproducibility:

bash
# Example Forge script command
forge script script/DeployGovernance.s.sol --rpc-url $RPC_URL --broadcast

Security considerations are paramount. Use established libraries like OpenZeppelin Governor for audited base contracts. Ensure the proposalThreshold is set to prevent spam. The quorum should be a function of circulating supply to remain relevant. All critical protocol functions should be gated behind the onlyGovernance or onlyTimelock modifier. Before going live, conduct extensive testing on a testnet, simulating full proposal lifecycles and edge cases. Document the governance process clearly for token holders, including how to interact with the front-end and directly with the contracts via Etherscan.

core-components
ON-CHAIN GOVERNANCE

Core Contract Components

Key smart contract modules required to implement a secure, decentralized governance system for finalizing outcomes or decisions on-chain.

01

Governance Token Contract

The foundation of voting power. This ERC-20 or ERC-1155 contract defines the voting token, manages its distribution, and often includes staking or delegation logic. Key considerations include tokenomics (inflation, vesting) and whether to use a vote-escrow model (like veTokens) to align long-term incentives. This contract must integrate with the Governor contract to check voting power.

02

Governor Contract (e.g., OpenZeppelin Governor)

The core state machine that manages proposal lifecycle. This contract handles:

  • Proposal creation with calldata for execution.
  • Voting period configuration (delay, duration).
  • Vote tallying and quorum calculation.
  • Proposal execution or cancellation. Frameworks like OpenZeppelin Governor provide modular bases (GovernorCountingSimple, GovernorVotes) that you extend, reducing audit surface area.
03

Timelock Controller

A critical security module that introduces a mandatory delay between a proposal's approval and its execution. This execution delay provides a safety window for users to exit the system if a malicious proposal passes. The Timelock contract becomes the sole executor (admin) of the governed contracts, enforcing that all state changes are first proposed and delayed. Use OpenZeppelin's TimelockController for a standard implementation.

04

Voting Strategy & Quorum

The logic that determines how votes are counted and what constitutes a valid outcome. This is often a separate contract (like GovernorVotesQuorumFraction) that defines:

  • Voting mechanism: e.g., simple majority, quadratic voting.
  • Quorum requirement: A minimum percentage of total voting power that must participate for a vote to be valid.
  • Vote snapshot: Using ERC20Votes or ERC721Votes to prevent double-spending votes during the voting period.
05

Proposal & Execution Logic

The target contracts containing the functions that will be called upon proposal execution. This is your protocol's core logic—like a Treasury, Configurator, or Upgrade Proxy. These contracts must:

  • Be owned by or have sensitive functions protected by the Timelock Controller address.
  • Expose clear, atomic functions for governance to call. Avoid complex, multi-step proposals in a single transaction to reduce risk.
06

Frontend & Indexing Integration

While not a smart contract, a functional system requires off-chain components. You'll need:

  • A frontend (e.g., using Tally or building custom) to create/view proposals and cast votes.
  • An indexer (like The Graph) to query proposal states, vote history, and delegate data efficiently.
  • Event listeners to track proposal creation, state changes, and execution on-chain.
step-by-step-implementation
ON-CHAIN GOVERNANCE

Step-by-Step Implementation

This guide details the technical process for implementing a smart contract system that finalizes outcomes through on-chain voting, using a modular approach suitable for DAOs and prediction markets.

Begin by defining the core governance contract that will manage proposal lifecycles. A typical structure includes a Governor contract that inherits from OpenZeppelin's Governor contracts (v4.9+). Key state variables to declare are the voting delay, voting period, proposal threshold, and quorum. For outcome finalization, you must also define an enum for proposal states (Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, Executed) and a mapping to store the proposed outcome data, which could be an address, a string, or a structured data hash.

The proposal creation function is triggered by an authorized proposer. It should encode the desired outcome—such as a recipient address for a treasury transfer or a new parameter value—into the proposal's calldata. Use propose() from the base Governor, which returns a proposalId. It's critical to emit a custom event logging the proposalId and the associated outcome data. This creates an immutable link between the vote and the specific action to be executed upon success, forming the basis for finalization.

Voting logic is handled by the token-weighted castVote functions. For robust outcome finalization, consider implementing vote tracking that records each voter's choice relative to the specific outcome. After the voting period ends, the state() function determines if the proposal reached quorum and a majority. A Succeeded state means the encoded outcome is approved for execution. At this stage, the proposal's data is considered finalized on-chain; the result is immutable and publicly verifiable by any external contract or interface.

Execution is the final step that enacts the will of the voters. Call the execute() function, which processes the stored calldata to perform the action, such as transferring funds or updating a contract variable. Implement a require statement to ensure execution can only occur for proposals in the Succeeded state. For extra security, add a timelock contract between the Governor and the target. This introduces a delay between finalization and execution, allowing users to react if they disagree with the outcome's implementation.

To make the system usable, you must build an interface that fetches and displays proposal data. Use The Graph or direct contract calls to index events and query proposal states. A frontend should clearly show: the proposed outcome, current vote tally, time remaining, and finalization status. For developers, provide a script using Ethers.js or Viem to create and execute proposals programmatically. Always test the entire flow on a testnet like Sepolia using tools like Foundry (forge test) to simulate voting and execution paths.

oracle-integration
TUTORIAL

Integrating Oracle Data as a Voting Reference

This guide explains how to securely incorporate external data from oracles into on-chain governance mechanisms for finalizing decisions.

On-chain governance systems often need to make decisions based on real-world events, such as the outcome of a sports match, the price of an asset, or the result of an external vote. To finalize these outcomes trustlessly, the smart contract requires a reliable source of truth. This is where oracles become essential. Oracles like Chainlink, Pyth Network, and API3 act as secure middleware, fetching, verifying, and delivering external data onto the blockchain. Integrating their data as a voting reference transforms subjective proposals into objective, executable conditions.

The core technical challenge is ensuring the governance contract only accepts data from a pre-defined, trusted oracle source. This is typically done by storing the oracle's contract address and the specific data feed ID (or job ID) within the governance contract's state. When a proposal is created, it includes the expected data conditions. After the voting period ends and the proposal passes, a finalization function is called. This function requests the latest data from the specified oracle feed and compares it against the proposal's conditions to determine the final outcome.

Here is a simplified Solidity example for a governance contract that uses a Chainlink Data Feed to check if an asset price is above a certain threshold to execute a proposal's action.

solidity
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract OracleGovernance {
    AggregatorV3Interface internal priceFeed;
    uint256 public executionThreshold;

    constructor(address _oracleAddress) {
        priceFeed = AggregatorV3Interface(_oracleAddress);
    }

    function finalizeProposal() external {
        // Fetch the latest price from the oracle
        (, int256 price, , , ) = priceFeed.latestRoundData();
        
        // Execute the proposal logic only if the condition is met
        if (uint256(price) >= executionThreshold) {
            // Execute the approved proposal's action
            _executeAction();
        }
    }

    function _executeAction() internal {
        // Implementation of the proposal's action
    }
}

This pattern ensures execution is contingent on verified real-world data.

For more complex conditions, consider using Chainlink Functions or a custom oracle solution to fetch and compute data from any API. The key security considerations are source authenticity (verifying the data origin), data freshness (using recent timestamps), and decentralization (using multiple oracle nodes to avoid a single point of failure). Always validate the oracle's response within the contract, checking for stale data or error codes returned by the latestRoundData function.

This integration pattern is used in prediction markets like Polymarket to resolve events, in DeFi protocols for parameter adjustments based on market conditions, and in DAO treasuries for executing trades when certain price levels are hit. By anchoring governance to oracle data, communities can automate the execution of complex, conditional proposals with a high degree of reliability and without requiring a trusted intermediary to interpret the result.

security-considerations
SECURITY AND ANTI-MANIPULATION MEASURES

Setting Up On-Chain Governance for Outcome Finalization

On-chain governance provides a transparent, tamper-resistant framework for finalizing critical outcomes like dispute resolutions or protocol upgrades, directly mitigating manipulation risks.

On-chain governance moves decision-making authority from a centralized entity to a decentralized network of token holders. This is achieved by encoding governance rules into smart contracts on a blockchain, typically using standards like OpenZeppelin's Governor contracts. When a proposal—such as finalizing the result of a prediction market or a dispute—is submitted, token holders vote directly with their stakes. The outcome is executed automatically by the contract based on the vote, removing human intermediaries and their potential for bias or corruption. This creates a verifiable and immutable record of the decision process.

To resist manipulation, governance systems implement several key mechanisms. Vote delegation allows less active participants to delegate their voting power to experts, improving participation without requiring constant engagement. Time-locks enforce mandatory delays between a proposal's creation, voting period, and execution, preventing rushed decisions and allowing for community scrutiny. Quorum requirements mandate a minimum percentage of the total token supply to participate in a vote for it to be valid, protecting against low-turnout attacks where a small, coordinated group could sway results.

A common implementation uses a three-step process: 1) Proposal Submission, where a transaction creates a proposal with executable calldata; 2) Voting Period, a fixed timeframe (e.g., 3-7 days) where holders cast votes; 3) Execution, where the successful proposal's calldata is run. For example, finalizing a prediction market outcome might involve a proposal that calls the market contract's resolve function. The smart contract code ensures that only proposals passing the predefined vote threshold (e.g., majority >50%) and quorum are executed.

Security audits are critical before deploying governance contracts. Common vulnerabilities include vote manipulation through token flash loans, where an attacker borrows a large stake to sway a vote and returns it after, and proposal lifecycle flaws that could allow canceled proposals to be executed. Using battle-tested, audited frameworks like Compound's Governor Bravo or OpenZeppelin Governor reduces these risks. Regular security upgrades via the governance process itself ensure the system can adapt to new threats.

For maximum resistance to manipulation, combine on-chain governance with other measures. Multi-sig timelocks can add a final review layer for high-stakes executions, where a council of signers can veto a malicious proposal that passed technically but is against community interest. Snapshot can be used for off-chain, gas-free sentiment signaling before an on-chain vote is created, refining proposal quality. This layered approach balances decentralization, security, and practical efficiency for finalizing critical on-chain outcomes.

CONFIGURATION MATRIX

Governance Parameter Trade-offs

Trade-offs between key on-chain governance parameters for outcome finalization, based on implementations from Compound, Uniswap, and Aave.

ParameterHigh Security (Conservative)High Participation (Progressive)Fast Execution (Agile)

Voting Delay

2 days

1 day

6 hours

Voting Period

7 days

3 days

2 days

Proposal Threshold

0.5% of supply

0.1% of supply

0.25% of supply

Quorum Required

4% of supply

2% of supply

1.5% of supply

Timelock Delay

2 days

null

12 hours

Emergency Execution

Vote Delegation

Proposal Bond

0.1 ETH

0.05 ETH

0.2 ETH

testing-and-deployment
TESTING AND DEPLOYMENT STRATEGY

Setting Up On-Chain Governance for Outcome Finalization

A guide to implementing and testing a secure, on-chain governance system for finalizing protocol decisions, using Solidity and Foundry.

On-chain governance systems allow token holders to vote directly on protocol changes, such as parameter updates or treasury allocations, with results finalized and executed automatically via smart contracts. Unlike off-chain signaling, this creates a cryptographically verifiable and enforceable record. A typical flow involves: a proposal creation period, a voting period where votes are weighted by token balance, and an execution phase where the proposal's logic runs if it passes. Setting this up requires careful consideration of quorum requirements, vote weighting mechanisms (e.g., token-weighted, quadratic), and timelocks for security.

Start by writing the core governance contract. Use OpenZeppelin's Governor contracts (v4.9+) as a secure foundation. You'll need to implement the IGovernor interface and configure key parameters like votingDelay, votingPeriod, and proposalThreshold. The proposal's execution logic is encapsulated in the execute function, which calls predefined functions on other contracts. For testing, use a mock token contract that implements the IVotes interface for snapshot-based voting. Here's a basic test setup in Foundry:

solidity
function test_ProposalExecution() public {
    // 1. Create a proposal to update a parameter in a TargetContract
    uint256 proposalId = governor.propose(
        [address(target)],
        [0],
        [abi.encodeCall(TargetContract.setValue, (newValue))],
        "Update protocol fee"
    );
    // 2. Simulate voting by delegating tokens and casting votes
    vm.roll(block.number + governor.votingDelay() + 1);
    governor.castVote(proposalId, 1); // 1 = For
    // 3. Move past voting period and execute
    vm.roll(block.number + governor.votingPeriod() + 1);
    governor.execute(...);
    // 4. Assert the state change
    assertEq(target.getValue(), newValue);
}

A robust deployment strategy involves multiple stages. First, deploy all contracts to a testnet (like Sepolia or Goerli) and run comprehensive integration tests. Use Foundry's forge test with -vvv for verbose logging to trace calls and events. Key test scenarios include: proposal creation with insufficient weight, voting with delegated tokens, reaching quorum, failed execution due to reverts, and timelock delays. After testing, consider a phased mainnet rollout: deploy the governance contract in a paused or timelocked state, grant control to a multisig of trusted community members initially, and then gradually decentralize control by transferring ownership to the governance contract itself after a successful trial period.

Security is paramount. Common vulnerabilities include vote manipulation through token flash loans, so consider using snapshot blocks (block.number - 1) for vote weighting. Ensure the execute function has strict access controls and uses a timelock contract (like OpenZeppelin's TimelockController) to delay execution, giving users time to exit if a malicious proposal passes. Audit all proposal target functions for reentrancy and logic errors, as they will be called with the governor's privileges. Tools like Slither or Mythril can perform static analysis. Finally, monitor the system on mainnet using Tenderly or OpenZeppelin Defender to track proposal states and vote counts in real-time.

ON-CHAIN GOVERNANCE

Frequently Asked Questions

Common technical questions and troubleshooting steps for developers implementing outcome finalization using on-chain governance.

In on-chain governance for outcome finalization, the token's function is strictly defined. A governance token confers voting rights to propose, debate, and ratify outcomes (e.g., releasing funds, confirming a milestone). Its primary utility is voting power. A utility token might be used to pay for services within the system (e.g., submitting data, requesting audits) but does not grant governance rights. For finalization, you need a governance token. A common mistake is using an ERC-20 with no voting logic; you must implement or integrate a standard like OpenZeppelin's Governor contracts, which define proposal lifecycle and vote counting.

conclusion
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

This guide has outlined the core components for implementing a secure on-chain governance system to finalize off-chain outcomes. The next steps involve rigorous testing and exploring advanced patterns.

You now have a functional blueprint for an on-chain governance system. The core contract architecture includes a Governance contract for proposal lifecycle management, a Treasury for fund custody, and a VoteToken for staking and delegation. The critical finalizeOutcome function acts as the secure bridge, executing only proposals that have passed a quorum and majority vote. Remember to deploy these contracts in a logical order: first the token, then the treasury (pointing to the token), and finally the governance contract (pointing to both).

Before considering a mainnet deployment, thorough testing is non-negotiable. Use a development framework like Foundry or Hardhat to write comprehensive unit and integration tests. Key scenarios to cover include: - Testing vote tallying under various quorum conditions - Simulating malicious proposals that attempt to drain the treasury - Ensuring the onlyGovernance modifier correctly restricts access to privileged functions. Consider using a testnet like Sepolia or Goerli for dry runs, and employ tools like Tenderly or OpenZeppelin Defender to monitor for unexpected behavior.

For production systems, explore advanced governance patterns to enhance security and flexibility. Time-locks (e.g., OpenZeppelin's TimelockController) introduce a mandatory delay between a proposal's approval and its execution, giving token holders a final chance to react. Multisig guardians can be added as a fallback mechanism to veto malicious proposals that somehow pass. For complex decision-making, look into snapshot voting with on-chain execution, where votes are cast gas-efficiently off-chain and only the final merkle proof is submitted on-chain.

The final step is active maintenance. Governance parameters like quorumPercentage and votingDelay are not set in stone. The system should include a meta-governance process to adjust these parameters over time based on community feedback and participation metrics. Keep all contract logic upgradeable via transparent proxy patterns (like UUPS) to patch vulnerabilities or add features, but ensure the upgrade mechanism itself is under strict governance control to maintain decentralization.