Skip to content

Governance: Votes#552

Merged
brozorec merged 17 commits intomainfrom
votes
Feb 11, 2026
Merged

Governance: Votes#552
brozorec merged 17 commits intomainfrom
votes

Conversation

@brozorec
Copy link
Collaborator

@brozorec brozorec commented Jan 14, 2026

Fixes #437
Fixes #439

PR Checklist

  • Tests
  • Documentation

Summary by CodeRabbit

Release Notes

  • New Features
    • Added voting module with delegation, checkpoints, and historical vote tracking capabilities
    • Added voting extensions for fungible and non-fungible tokens
    • Added example contract demonstrating fungible token voting
    • Updated governance documentation with voting concepts and timelock usage details

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Introduces a comprehensive voting system for the Soroban ecosystem with delegation, checkpointing, and voting unit tracking. Adds governance votes module with historical vote queries, fungible and non-fungible token voting extensions integrating with voting infrastructure, and an example contract demonstrating usage.

Changes

Cohort / File(s) Summary
Governance Voting Core
packages/governance/src/lib.rs, packages/governance/src/votes/mod.rs, packages/governance/src/votes/storage.rs, packages/governance/src/votes/test.rs
Introduces votes module with contract trait Votes exposing delegation and historical vote queries. Storage layer implements checkpoint-based vote tracking, binary search for past votes, delegation state management, and voting unit transfers. Test module provides comprehensive coverage of voting mechanics, delegation, transfers, and edge cases.
Fungible Token Voting Extension
packages/tokens/src/fungible/extensions/mod.rs, packages/tokens/src/fungible/extensions/votes/mod.rs, packages/tokens/src/fungible/extensions/votes/storage.rs, packages/tokens/src/fungible/extensions/votes/test.rs
Adds FungibleVotes extension implementing ContractOverrides to integrate voting unit tracking into mint, burn, transfer, and transfer_from operations. Storage delegates token operations to Base while synchronizing voting units. Tests verify voting unit updates across delegation scenarios.
Non-Fungible Token Voting Extension
packages/tokens/src/non_fungible/extensions/mod.rs, packages/tokens/src/non_fungible/extensions/votes/mod.rs, packages/tokens/src/non_fungible/extensions/votes/storage.rs, packages/tokens/src/non_fungible/extensions/votes/test.rs
Adds NonFungibleVotes extension for NFT voting with voting weight of 1 unit per NFT. Implements ContractOverrides for transfer and transfer_from, plus mint, sequential_mint, burn, and burn_from with voting unit synchronization. Test coverage includes multi-NFT delegation and voting aggregation.
Example Contract
examples/fungible-votes/...
New fungible-votes example demonstrating contract lifecycle with owner-controlled mint, FungibleToken, Votes, and Ownable trait implementations. Includes Cargo.toml configuration and lib.rs setup.
Module Exports & Dependencies
packages/governance/README.md, packages/tokens/src/fungible/mod.rs, packages/tokens/src/non_fungible/mod.rs, packages/tokens/Cargo.toml, Cargo.toml
Updates public re-exports to expose votes extensions. Documents Votes subsection with Core Concepts and Features. Expands Timelock documentation with usage examples. Adds stellar-governance workspace dependency and fungible-votes workspace member.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Account as Account Storage
    participant Votes as Votes Module
    participant Delegate as Delegate Tracking
    participant Checkpoints as Checkpoint Storage
    participant History as Historical Queries

    User->>Votes: delegate(account, delegatee)
    Votes->>Account: verify authorization
    Votes->>Delegate: update delegatee reference
    Votes->>Checkpoints: push checkpoint for old delegate
    Votes->>Checkpoints: push checkpoint for new delegate
    Votes->>User: emit DelegateChanged

    User->>Votes: transfer_voting_units(from, to, amount)
    Votes->>Delegate: get old_delegate (from)
    Votes->>Delegate: get new_delegate (to)
    Votes->>Checkpoints: update old_delegate checkpoint
    Votes->>Checkpoints: update new_delegate checkpoint
    Votes->>User: emit DelegateVotesChanged

    User->>History: get_past_votes(account, timepoint)
    History->>Checkpoints: binary search checkpoint at timepoint
    History->>User: return historical voting power
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Poem

🐰 Hop-hop, voting power's here!
Delegation tracked with checkpoint cheer,
From fungible coins to NFTs bright,
Voting units sync both day and night! 🗳️✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Governance: Votes' is concise and accurately describes the main feature introduced in the PR—a votes module for governance.
Description check ✅ Passed The PR description references fixed issues (#437 and #439) and marks checklist items as complete, meeting the template requirements despite minimal additional context.
Linked Issues check ✅ Passed The code changes comprehensively implement voting power tracking [#437], delegation with checkpoints, and both fungible and non-fungible token voting extensions [#439].
Out of Scope Changes check ✅ Passed All changes are directly aligned with implementing the votes module, governance extensions, and supporting infrastructure with no unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch votes

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Jan 27, 2026

Codecov Report

❌ Patch coverage is 98.93617% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.24%. Comparing base (63167bb) to head (2fe0d53).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/governance/src/votes/storage.rs 98.56% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #552      +/-   ##
==========================================
+ Coverage   96.09%   96.24%   +0.14%     
==========================================
  Files          54       57       +3     
  Lines        5225     5507     +282     
==========================================
+ Hits         5021     5300     +279     
- Misses        204      207       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@brozorec brozorec marked this pull request as ready for review January 28, 2026 10:59
@brozorec brozorec requested a review from ozgunozerk January 28, 2026 10:59
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/governance/src/votes/storage.rs`:
- Around line 258-312: The transfer_voting_units function currently calls
move_delegate_votes twice (once for sender and once for receiver), which emits
misleading zero-net events when both accounts delegate to the same address;
change the flow to first compute from_delegate = get_delegate(e, from_addr) and
to_delegate = get_delegate(e, to_addr) (for Option cases) and then invoke
move_delegate_votes once with those delegate Option<&Address> values so its
internal from==to short-circuit prevents redundant events; ensure you still
perform voting unit arithmetic (get_voting_units, checked_sub/checked_add,
set_voting_units) and still call push_total_supply_checkpoint for mint/burn
paths, but move the delegate lookup so a single call to move_delegate_votes
replaces the two separate calls.
🧹 Nitpick comments (3)
packages/governance/README.md (1)

118-121: Consider adding the fungible-votes example.

The Examples section only references the timelock-controller example. Consider adding a reference to the new examples/fungible-votes/ example to showcase the Votes module usage.

📝 Suggested addition
 ## Examples
 
 See the following examples in the repository:
+- [`examples/fungible-votes/`](https://github.com/OpenZeppelin/stellar-contracts/tree/main/examples/fungible-votes) - Fungible token with voting power delegation
 - [`examples/timelock-controller/`](https://github.com/OpenZeppelin/stellar-contracts/tree/main/examples/timelock-controller) - Timelock controller with role-based access control
examples/fungible-votes/src/contract.rs (1)

1-8: Unused import: MuxedAddress.

The MuxedAddress type is imported but not used in this file.

♻️ Suggested fix
-use soroban_sdk::{contract, contractimpl, Address, Env, MuxedAddress, String};
+use soroban_sdk::{contract, contractimpl, Address, Env, String};
packages/governance/src/votes/storage.rs (1)

316-324: Add extend_ttl after persistent storage writes to prevent premature expiration of voting data.

The codebase consistently calls extend_ttl after .get() operations (visible in get_delegate, get_voting_units, get_checkpoint, get_total_supply_checkpoint), but does not call it after .set() operations in set_voting_units, delegate, or checkpoint creation/updates. Since .set() initializes entries with the network minimum TTL, there's a window where newly written data could expire before being read and extended. For critical historical voting data, extend TTL immediately after writes to ensure consistent persistence.

♻️ Example tweak for this helper
 fn set_voting_units(e: &Env, account: &Address, units: u128) {
     let key = VotesStorageKey::VotingUnits(account.clone());
     if units == 0 {
         e.storage().persistent().remove(&key);
     } else {
         e.storage().persistent().set(&key, &units);
+        e.storage().persistent().extend_ttl(&key, VOTES_TTL_THRESHOLD, VOTES_EXTEND_AMOUNT);
     }
 }

Apply the same pattern to delegate() and checkpoint creation in push_checkpoint and push_total_supply_checkpoint.

Copy link
Collaborator

@ozgunozerk ozgunozerk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Liked the overall structure. Few changes are requested. All of them are fairly minimal.

Only reviewed the half of it. Will continue to review tomorrow. Reminder to myself: push_checkpoint

Copy link
Collaborator

@ozgunozerk ozgunozerk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One structural/architectural change for fungible and non-fungible tokens is commented.

@brozorec
Copy link
Collaborator Author

  • Removed code duplication and function variants for account and total supply tracking
  • Renamed get_past_total_supply and added a new fn to the trait get_total_supply

Thanks for the suggestions, now it looks much better 🙏

@brozorec brozorec requested a review from ozgunozerk February 10, 2026 11:08
@ozgunozerk
Copy link
Collaborator

Before merging, let's try to hit the target for codecov, amazing work 💯

@brozorec brozorec merged commit 606ec84 into main Feb 11, 2026
7 checks passed
@brozorec brozorec deleted the votes branch February 11, 2026 08:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fungible and Non-Fungible Votes Extensions Votes

2 participants

Comments