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 Governance Token Staking for Voting Rights

A technical tutorial for implementing a veToken-like system where users lock tokens to gain time-weighted voting power in DAO governance.
Chainscore © 2026
introduction
GUIDE

Setting Up Governance Token Staking for Voting Rights

Learn how to stake governance tokens to participate in on-chain decision-making, from smart contract basics to practical implementation steps.

Governance staking is the mechanism that converts a simple token into a vote. By locking tokens in a designated smart contract, users signal their long-term commitment to a protocol and earn the right to participate in its governance. This process is fundamental to decentralized autonomous organizations (DAOs) like Uniswap, Compound, and Aave. The staked tokens, often represented as a derivative token (e.g., veCRV for Curve Finance), are your key to proposing changes, voting on proposals, and directing protocol incentives such as fee distribution or liquidity mining rewards.

The core technical component is the staking smart contract. A basic implementation involves two primary functions: stake(uint256 amount) and unstake(uint256 amount). When a user calls stake, the contract transfers their tokens from their wallet and mints an equivalent amount of voting power, typically tracked in an internal mapping like mapping(address => uint256) public votingPower. To prevent vote manipulation, many protocols implement a timelock or vesting mechanism, where voting power decays linearly over time or requires a cooldown period before unstaking, as seen with ve-token models.

Here is a simplified Solidity code snippet for a timelock staking contract:

solidity
contract GovernanceStake {
    IERC20 public governanceToken;
    mapping(address => uint256) public stakeBalance;
    mapping(address => uint256) public unlockTime;
    uint256 public constant LOCK_PERIOD = 30 days;

    function stake(uint256 amount) external {
        governanceToken.transferFrom(msg.sender, address(this), amount);
        stakeBalance[msg.sender] += amount;
        unlockTime[msg.sender] = block.timestamp + LOCK_PERIOD;
    }

    function getVotingPower(address user) public view returns (uint256) {
        return stakeBalance[user];
    }
}

This contract locks tokens for 30 days, during which the user's getVotingPower is equal to their staked balance.

Integrating staking with voting requires a separate voting contract that queries the staking contract. A proposal voting function will check the caller's voting power at the staking contract before allowing a vote to be cast. For example, a function castVote(uint256 proposalId, bool support) would first call stakingContract.getVotingPower(msg.sender) to determine the weight of the vote. This separation of concerns—staking versus voting logic—is a common and secure architectural pattern, allowing upgrades and flexibility in each module.

When implementing governance staking, key considerations include security, voter apathy, and whale dominance. Use audited, battle-tested code from libraries like OpenZeppelin for token handling. To mitigate low participation, some protocols like OlympusDAO use bonding mechanisms to incentivize staking. For decentralization, consider implementing vote delegation or quadratic voting models, which reduce the impact of large token holders. Always verify contract interactions on a testnet like Goerli or Sepolia before mainnet deployment.

The final step is front-end integration. Your dApp interface should connect a user's wallet (using libraries like ethers.js or viem), display their stake balance and voting power, and provide clear buttons to stake, unstake (when unlocked), and view active proposals. Tools like The Graph can index on-chain staking and voting events to create a historical dashboard. By completing this setup, you empower your community with transparent, on-chain governance, turning token holders into active protocol stewards.

prerequisites
GOVERNANCE TOKEN STAKING

Prerequisites and Setup

This guide covers the technical prerequisites for setting up a system where users stake governance tokens to earn voting rights, a core mechanism in DAOs and DeFi protocols.

Governance token staking is a mechanism that ties voting power directly to economic commitment. Unlike simple token holding, staking requires users to lock their tokens in a smart contract, which then grants them voting rights proportional to their stake. This design mitigates vote manipulation and Sybil attacks by increasing the cost of acquiring influence. The core smart contract logic typically involves a StakingVault that manages deposits, a VotingEscrow model for time-locked stakes, and a separate Governor contract that checks a user's staked balance before allowing them to propose or vote on governance proposals.

Before writing any code, you must define the tokenomics and voting parameters. Key decisions include: the staking token (e.g., your project's ERC-20 governance token), the lock-up period (if any, as seen in models like Curve's veToken), the voting power calculation (linear to stake, or boosted by lock time), and any rewards or penalties. You'll need development tools like Hardhat or Foundry, Node.js, and a wallet such as MetaMask. For testing, use a local Hardhat network or a testnet like Sepolia. Essential reference documentation includes the OpenZeppelin Contracts library for secure base implementations.

The foundational step is deploying the governance token itself, typically an ERC-20. Using OpenZeppelin's contracts ensures security and standards compliance. A basic deployment script in Hardhat might look like:

solidity
// scripts/deployToken.js
const hre = require("hardhat");
async function main() {
  const MyGovernanceToken = await hre.ethers.getContractFactory("MyGovernanceToken");
  const token = await MyGovernanceToken.deploy("Governance Token", "GT");
  await token.deployed();
  console.log("Token deployed to:", token.address);
}

You must also fund test wallets with this token and native ETH for gas to proceed with staking contract interactions.

Next, you'll implement the staking vault. This contract must safely custody user tokens and track their stake to calculate voting power. A minimal StakingVault needs functions for stake(uint256 amount), withdraw(uint256 amount), and a view function getVotingPower(address user). Critical security considerations include using the checks-effects-interactions pattern to prevent reentrancy, ensuring proper access control (often onlyOwner for pausing), and emitting events for all state changes. Always write comprehensive tests for edge cases like zero-value stakes, overflows, and reward calculations if applicable.

Finally, integrate the staking vault with a governance module. The OpenZeppelin Governor contract suite is the industry standard. You will extend a contract like GovernorCompatibilityBravo and override the _getVotes function to query voting power from your StakingVault instead of the raw token balance. This linkage is what transforms staked tokens into governance authority. After deployment, you must verify your contracts on a block explorer like Etherscan and create front-end interactions using a library like Wagmi or ethers.js to allow users to connect their wallets, view their stake, and cast votes.

core-mechanics-explanation
VE TOKEN MODEL

Setting Up Governance Token Staking for Voting Rights

Learn how to lock your governance tokens to gain voting power and direct protocol incentives using the veToken model.

The veToken (vote-escrowed token) model transforms a standard ERC-20 governance token into a non-transferable, time-locked asset that grants voting rights. Instead of a simple 1:1 vote per token, voting power is determined by the amount and duration of the lock. For example, locking 100 tokens for 4 years grants the maximum voting power, while a shorter lock yields proportionally less. This mechanism aligns long-term token holders with the protocol's success, as their influence is directly tied to their commitment. Protocols like Curve Finance and Balancer pioneered this model to govern liquidity mining emissions and fee distribution.

To participate, you must interact with the protocol's VotingEscrow smart contract. The core action is calling the create_lock function, which takes two main parameters: the _value (amount of tokens to lock) and the _unlock_time (a future timestamp). Your tokens are transferred and locked in the contract, and you receive a non-fungible veNFT representing your position. This NFT is your key to voting and claiming rewards. It's crucial to understand that increasing your lock amount or extending its duration later requires a new transaction, often via an increase_amount or increase_unlock_time function.

Your voting power decays linearly over time until the unlock date, at which point it reaches zero and you can withdraw your base tokens. This creates a continuous incentive to re-lock to maintain influence. In practice, you use this power to vote on gauge weights, which determine how much of the protocol's token emissions are directed to specific liquidity pools. A typical voting flow involves: 1) connecting your wallet to the protocol's governance interface, 2) selecting your veNFT, 3) allocating voting points to your preferred liquidity gauges, and 4) submitting the vote transaction.

For developers, integrating veToken mechanics requires understanding key contract interactions. Here's a simplified example of creating a lock using ethers.js:

javascript
const votingEscrow = new ethers.Contract(contractAddress, abi, signer);
const tokenAmount = ethers.utils.parseEther("100");
// Set unlock time to 4 years from now
const unlockTime = Math.floor(Date.now() / 1000) + (4 * 365 * 86400);
const tx = await votingEscrow.create_lock(tokenAmount, unlockTime);
await tx.wait();

Always verify the lock duration parameters, as exceeding the maximum (e.g., 4 years) or setting a time in the past will cause the transaction to revert.

The primary benefit is the ability to direct protocol-owned liquidity and earn a share of trading fees or other rewards. However, consider the trade-offs: your capital is illiquid for the lock period, and you must actively participate in governance to maximize returns. Security is paramount; only interact with the official, audited contracts from the protocol's website. Before locking, use block explorers to check the contract's verification status and review community resources like the Curve DAO documentation for the latest implementation details.

ARCHITECTURE

Governance Staking Model Comparison

Comparison of common staking models for on-chain governance, detailing their technical trade-offs for security, participation, and flexibility.

FeatureDirect StakingVote-Escrow (veTokens)Liquid Staking Derivatives (LSDs)

Voting Power Calculation

Linear (1 token = 1 vote)

Time-locked (power = tokens * lock duration)

Linear (1 derivative = 1 vote)

Capital Efficiency

Voter Turnout Incentive

Direct token rewards

Boosted rewards & fee share

Underlying staking rewards

Protocol Revenue Capture

Average Lockup Duration

Flexible (0 days)

Long-term (1-4 years)

Flexible (0 days)

Exit Liquidity / Unstaking Delay

< 1 sec (Unbonding)

Lock period (e.g., 4 years)

Instant (via DEX)

Typical Implementation

Compound, Uniswap

Curve, Frax Finance

Lido, Rocket Pool

Key Security Consideration

Whale dominance

Long-term alignment

Derivative issuer centralization

contract-implementation-steps
SOLIDITY DEVELOPMENT

Step 1: Implementing the Staking Contract

This guide walks through creating a foundational staking contract that locks governance tokens to grant voting power, using Solidity and OpenZeppelin libraries.

Governance token staking is a core mechanism for aligning voter incentives and securing decentralized decision-making. By requiring users to lock their tokens in a smart contract, the system ensures that voting power is proportional to economic commitment and skin-in-the-game. We'll build a contract that allows users to stake, unstake, and query their votingPower. The contract will mint non-transferable stakingShares to represent a user's stake and track the total locked supply. This design prevents double-voting and sybil attacks by tying voting rights directly to the staked balance.

We start by importing and inheriting from OpenZeppelin's audited contracts for security and gas efficiency. Use IERC20 to interface with the external governance token and ERC20 to create the internal staking share token.

solidity
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract GovernanceStaking is ERC20 {
    IERC20 public immutable governanceToken;
    uint256 public totalStaked;
    mapping(address => uint256) public stakedBalance;

The governanceToken is the external ERC-20 token users will stake. The stakedBalance mapping tracks each user's locked amount, while the inherited ERC-20 functionality manages the staking share balances.

The stake function is the primary entry point. Users must first approve the staking contract to spend their governance tokens. Upon calling stake, the contract transfers the tokens and mints an equivalent amount of staking shares.

solidity
function stake(uint256 amount) external {
    require(amount > 0, "Amount must be > 0");
    governanceToken.transferFrom(msg.sender, address(this), amount);
    stakedBalance[msg.sender] += amount;
    totalStaked += amount;
    _mint(msg.sender, amount); // Mint staking shares 1:1
}

The 1:1 minting ratio simplifies voting power calculations. The user's votingPower is directly equal to their balance of staking shares, which can be queried via the standard balanceOf function from the ERC-20 base contract.

Unstaking requires burning the staking shares to reclaim the underlying tokens. A common pattern is to enforce a timelock or cooldown period to prevent rapid withdrawal during critical governance votes. A basic implementation without a timelock is shown below.

solidity
function unstake(uint256 amount) external {
    require(amount > 0 && stakedBalance[msg.sender] >= amount, "Insufficient stake");
    stakedBalance[msg.sender] -= amount;
    totalStaked -= amount;
    _burn(msg.sender, amount); // Burn staking shares
    governanceToken.transfer(msg.sender, amount);
}

For production, integrate a timelock by recording a unlockTimestamp in a mapping and adding a withdraw function that executes after the delay. This prevents voters from exiting immediately after casting ballots.

The final step is integrating this staking contract with a governance module like OpenZeppelin Governor. The Governor contract uses an IVotes interface to read voting power. Our staking contract can comply by implementing the getVotes function, which returns a user's staking share balance at the current block.

solidity
function getVotes(address account) external view returns (uint256) {
    return balanceOf(account);
}

Deploy the staking contract, then configure your Governor contract to use it as the voting token. Users must now stake tokens to participate in proposals. Always conduct thorough testing and consider security audits before mainnet deployment, as this contract will hold user funds.

voting-power-calculation
IMPLEMENTATION

Step 2: Calculating and Querying Voting Power

This section details the technical process of calculating a user's voting power based on their staked tokens and how to query this data for governance proposals.

Voting power is the fundamental metric that determines a user's influence in an on-chain governance system. It is typically calculated as a function of the number of governance tokens a user has staked and the duration for which they have been locked. A common model, known as vote-escrow (veToken), increases voting power based on lock time, incentivizing long-term alignment. For example, locking 100 tokens for 4 years might grant 100 veTokens, while locking the same amount for 1 year might grant only 25 veTokens. This mechanism is used by protocols like Curve Finance and Frax Finance to weight votes toward long-term stakeholders.

To calculate voting power, your smart contract must maintain a mapping of user stakes and their associated lock timestamps. The core calculation often involves a linear decay function. A basic Solidity view function might look like this:

solidity
function getVotingPower(address user) public view returns (uint256) {
    Stake memory s = stakes[user];
    if (block.timestamp >= s.unlockTime) return 0;
    uint256 timeLeft = s.unlockTime - block.timestamp;
    uint256 maxLock = MAX_LOCK_TIME; // e.g., 4 years in seconds
    return (s.amount * timeLeft) / maxLock;
}

This function returns a dynamic veToken balance that decreases linearly as the unlock time approaches.

For efficient querying, especially by front-end interfaces, it's crucial to expose this data via both on-chain view functions and indexed events. When a user stakes, emits a Staked event with the user's address, amount, and unlock time. Off-chain indexers (like The Graph) can listen to these events to build a queryable subgraph. This allows dApps to fetch a user's current voting power or the total voting power for a specific proposal snapshot without incurring gas costs for the end-user, using a GraphQL query to a subgraph endpoint.

Integrating this voting power into a governance proposal contract, such as OpenZeppelin's Governor, requires overriding the getVotes function. This function is called when a proposal is created (to snapshot votes) and when a vote is cast. Your override should simply call your internal getVotingPower function. Ensure you use a snapshot block number to prevent manipulation; the contract should record the voting power at the proposal creation block, not at the time of voting.

solidity
function getVotes(address account, uint256 blockNumber) public view override returns (uint256) {
    return getPriorVotingPower(account, blockNumber); // Your logic to fetch historical stake data
}

Best practices for security and usability include: - Using a reliable oracle or Chainlink for time calculations to prevent timestamp manipulation. - Implementing a checkpoint system for voting power, similar to ERC-20Votes, to efficiently query historical balances. - Clearly documenting the vote-escrow formula so users can precisely predict their influence. - Adding a fallback mechanism in your front-end to query the contract directly if the indexer is down. Always audit the interaction between your staking contract and the governor contract to ensure no edge cases allow for double-counting or inflated power.

governance-platform-integration
GOVERNANCE EXECUTION

Step 3: Integrating with Snapshot or Tally

Connect your staking contract to a governance platform to enable token-weighted voting. This step transforms staked tokens into active governance power.

After deploying your staking contract, the next step is to link it to a governance platform like Snapshot or Tally. These platforms provide the user interface and voting infrastructure, while your smart contract acts as the source of truth for voting power. Instead of using raw token balances, these systems query your staking contract to determine each user's voting weight based on their staked balance at a specific block. This separation of concerns—staking logic on-chain, voting interface off-chain—is a common pattern for gas-efficient governance.

For Snapshot, integration is off-chain and requires no smart contract modifications. You create a new space on snapshot.org and configure the voting strategy. The key is to use the erc20-balance-of or erc20-with-balance strategy, pointing it to your staking contract address instead of the base token. When a proposal is created, Snapshot takes a snapshot of staked balances at a specified block number. Voters then sign messages off-chain to cast their votes, which are tallied based on their staked weight from that block.

For a fully on-chain experience with Tally, you typically deploy a governance module like OpenZeppelin's Governor. Here, your staking contract must implement a specific interface. The most straightforward method is to have your StakingToken (the token representing a staked position) also be an ERC20Votes-compatible token. This standard provides snapshots of balances, which the Governor contract uses for voting power. You would then point your Tally dashboard to the deployed Governor contract, which automatically reads voting power from the staked token.

A critical implementation detail is ensuring vote delegation works correctly. If your staking contract mints a liquid staking token (e.g., stTOKEN), users should be able to delegate the voting power of that token to themselves or others. This is often handled by making the staking token inherit from ERC20Votes or a similar snapshot-enabled token standard. The _afterTokenTransfer hook must be overridden to move voting power when the staked token is transferred, maintaining accurate governance representation.

Test the integration thoroughly before going live. On Snapshot, create a test proposal in a demo space and verify that voting weights pulled from your contract are correct. For Tally, run proposals through a testnet Governor and confirm that users can propose, vote, and execute transactions based on their staked balance. This step finalizes the loop: users stake for rewards and simultaneously activate their governance rights within a professional platform.

GOVERNANCE TOKEN STAKING

Common Implementation Questions

Answers to frequent technical questions and troubleshooting steps for developers implementing on-chain governance with staked voting power.

This is a common architectural decision, not a bug. Most staking-for-voting systems calculate voting power based on a snapshot of staked balances at a specific block. The logic typically checks msg.sender against a stored snapshot, not the delegate. To fix this, you must ensure the voting function accepts a signature from the delegate or implements a meta-transaction pattern. Review the Compound Governor Bravo or OpenZeppelin Governor contracts, which handle delegation by tracking token balances at the proposal's snapshot block, allowing the delegate to vote directly.

security-considerations
SECURITY AND ECONOMIC CONSIDERATIONS

Setting Up Governance Token Staking for Voting Rights

Implementing a staking mechanism for governance tokens requires careful design to balance security, economic incentives, and decentralization. This guide covers the critical considerations for developers.

Governance token staking is a mechanism where users lock their tokens in a smart contract to earn the right to vote on protocol proposals. This design serves two primary purposes: it sybil-resistance by making vote-buying attacks economically costly, and it aligns incentives by ensuring voters have "skin in the game." A common implementation is a time-lock, where tokens are locked for a minimum period (e.g., 7-30 days) to qualify for voting power. The staking contract must be non-custodial, allowing users to withdraw their principal after the lock-up expires, while the accrued voting rights are typically non-transferable to prevent delegation market complexities.

The economic security of the system hinges on the cost of attack. An attacker must acquire and stake enough tokens to pass a malicious proposal. The required capital is the stake_required multiplied by the token's market price. For example, if a proposal needs 1 million votes to pass and tokens are $10 each, the attack cost is $10 million. However, this is a static analysis. Developers must also model vote dilution from new stakers and the time value of locked capital. A 30-day lock at a 5% estimated annual yield represents an opportunity cost, increasing the real attack cost beyond the simple token price.

From a smart contract security perspective, the staking logic is a high-value target. Key risks include reentrancy on withdrawal functions, incorrect vote power accounting, and flash loan attacks to temporarily manipulate voting weight. Use established libraries like OpenZeppelin's ReentrancyGuard and implement a checks-effects-interactions pattern. Voting power should be calculated as a snapshot at the proposal creation block to prevent last-minute staking swings. All state changes must emit events for off-chain indexing. Regular audits from firms like Trail of Bits or ConsenSys Diligence are non-negotiable for mainnet deployment.

The staking mechanism directly impacts voter participation and decentralization. If the lock-up period is too long or the gas costs for staking are high, you risk low participation, concentrating power among a few large holders. Consider implementing gasless staking via meta-transactions or bundlers, or using Layer 2 solutions like Arbitrum or Optimism to reduce costs. The economic model should reward long-term alignment; some protocols implement a vote-escrow model (veToken), where voting power decays over time unless the lock is extended, creating a continuous commitment from participants.

Finally, integrate the staking contract with your governance framework. The staking contract should expose a getVotes(address account, uint256 blockNumber) function compatible with Governor-based systems (e.g., OpenZeppelin Governor). The governance contract will query this to determine voting power. Ensure there is a clear emergency unlock mechanism controlled by a timelocked multi-sig to handle critical bugs, but design it to be unusable for altering live votes. Document the entire flow for users, from staking on a frontend like Tally or Boardroom to casting votes, emphasizing the security of their locked funds.

GOVERNANCE TOKEN STAKING

Troubleshooting and Testing

Common issues and solutions for developers implementing on-chain governance with staked voting power.

This is a common architectural oversight. In many governance models, voting power is derived from a snapshot of staked balances at a specific block. If your contract only checks the staking balance of the direct caller (msg.sender), it will fail for delegated votes.

Solution: Implement a getVotes(address account, uint256 blockNumber) function that reads from a checkpointed history. Use OpenZeppelin's ERC20Votes or a similar pattern. The voting logic should query this function, not the live balanceOf. For example:

solidity
function castVote(uint256 proposalId, uint8 support) public override returns (uint256) {
    // Correct: Use historical voting power
    uint256 votes = getVotes(msg.sender, proposalSnapshot(proposalId));
    require(votes > 0, "GovernorVotes: no voting power");
    // ... voting logic
}
conclusion-next-steps
IMPLEMENTATION CHECKLIST

Conclusion and Next Steps

You have now configured the core components for a governance token staking system. This section summarizes key security considerations and outlines pathways for further development.

A secure staking contract is the foundation of any on-chain governance system. Key implementation checks include: verifying the staking token is non-transferable during the lock-up period, ensuring vote delegation logic correctly prevents double-voting, and implementing a timelock or emergency pause function for protocol upgrades. Always conduct a formal audit from a firm like OpenZeppelin or Trail of Bits before mainnet deployment. Use tools like Slither or MythX for preliminary static analysis.

For production systems, consider enhancing the basic staking model. Implement a slashing mechanism to penalize malicious actors, though this requires carefully defined and verifiable faults. Introduce tiered voting power, where longer lock-ups grant quadratic or otherwise increased voting weight, to incentivize long-term alignment. Utilize snapshot strategies via tools like Snapshot.org for gas-free voting on complex proposals, while keeping the stake-and-vote mechanism for critical on-chain upgrades.

The next step is integrating your staking contract with a governance framework. For a self-contained system, you can build a Governor contract using OpenZeppelin's Governor suite, which provides modules for voting, timelocks, and proposal management. Alternatively, you can connect your staked token balance to an existing DAO platform like Aragon or Tally. Ensure your getVotes function interface is compatible with your chosen governor, typically returning the voting power at a given block number for snapshot-based voting.

To engage your community, develop clear documentation and front-end interfaces. Provide a staking dashboard that displays: current APY/rewards, active proposals, and individual voting power. Use The Graph to index staking and voting events for efficient querying. Educational resources are crucial; create tutorials for delegating votes and explain the governance process. Transparent communication about treasury management and proposal outcomes builds the trust necessary for a decentralized governance system to thrive.