# Quorum Delegation
**Authors:** BlockScience and SDF, July 2023
### Summary
Quorum Delegation (QD) allows users to **passively vote** by delegating their choice to a group of users.
Unlike traditional delegation (1:1), QD allows **distributed delegation** across multiple users.
- Users choose and **rank a set of delegates**.
- If enough ranked users vote, their decision forms a **Quorum Vote**.
- The Quorum Vote is **cast automatically** for the user.
- QD serves as the **first layer** in **Neural Governance**.
This design reduces user attention costs while allowing **high flexibility** in delegation.
#### Visualization

In this example:
- The quorum consists of **10 anonymous users**.
- **6 vote "Yes", 2 vote "No", 1 abstains, and 1 does not participate**.
- The quorum participation threshold is **3/5 (6 users)**.
- Since "Yes" exceeds **simple majority** (4 votes), the user **automatically votes "Yes"**.
---
#### Use Cases
- **Trust-Set:** Delegate to personally trusted individuals.
- **Domain-Specific Bloc:** Delegate to topic experts.
- **Balanced Bloc:** Delegate across multiple fields (e.g., development, finance, social impact).
---
#### User Journey
#### On the UI:
1. **User chooses to delegate or actively vote.**
2. If **delegating**, user selects up to `MAX_QUORUM_CANDIDATES`.
3. **Backend resolves quorum after active votes are tallied:**
- Removes abstainers & delegates.
- If remaining candidates `< QUORUM_SIZE`, fills with abstainers.
4. **Backend calculates user vote:**
- Vote is determined by **absolute & relative consensus** within the quorum.
---
#### Module-Specific Adjustments
##### **Parametric Adjustments**
| Parameter | Description |
|-----------|------------|
| `MAX_QUORUM_CANDIDATES` | Max size of potential candidates a user can select for their Quorum. |
| `QUORUM_SIZE` | Max number of candidates considered in a Quorum Decision Vote. |
| `THRESHOLD_QUORUM_PARTICIPATION` | Minimum % of quorum participants needed for a valid vote. |
| `THRESHOLD_RELATIVE_AGREEMENT` | Minimum % of agreement required among active votes. |
##### **Logic Adjustments**
- **Allow/disallow re-delegation.**
- **Enable public pre-set quorums.**
- **Allow users to override quorum votes (e.g., waiting period, notification).**
- **Let users set all parameters themselves.**
##### **Functional Adjustments**
- **Generalizing QD beyond Yes/No/Abstain.**
- **Adding pre-selected quorums (e.g., Dev Quorum, Social Quorum).**
- **Changing vote weight logic (e.g., using Voting Power instead of 1 vote per candidate).**
- **Tracking delegation as a Neuron input (users with more delegations get higher voting power).**
- **Making Quorum candidates publicly visible.**
## Specification
**Authors:** BlockScience and SDF, July 2023
#### Introduction
A notebook containing an end-to-end example implementation for this document can be found on the [BlockScience/scf-voting-mechanism GitHub repository](https://github.com/BlockScience/scf-voting-mechanism).
#### General Definitions
The admissible user actions for the SDF-Voting Mechanism in each round are (mutually exclusive):
- Vote (yes/no) and Refrain from Voting (abstain) on any number of project submissions
- A "no" vote will cancel a "yes" vote with the same voting power. In other words, if someone has a Voting Power of 5, then Yes would add +5 to the Project Votes, No would add -5 and Abstain would add 0.
- Delegate full Voting Power to an ordered list of at least `n` users - called a Quorum. Quorums are only valid for the current round and should be re-initialized / re-activated for each new round.
- The Actual Quorum will consist of the first `n` users that opted for Voting rather than Delegating. If there are less than `n` users any missing slot will be replaced by Abstain votes.
#### Logic
Quorum Delegation is a novel delegation scheme inspired by the Stellar Consensus Protocol in which individual users privately select groups of other users that will indirectly determine the user's vote (as long as the group achieves internal consensus).
A user can select up to `n` other UUIDs (order sensitive) for creating their Quorum Candidates. The first-ranked `n` candidates that opted for Voting during the Round are going to be the Actual Quorum. Users that opted for Delegating are not taken into account for the Actual Quorum (to softly avoid issues around re-delegation).
Depending on the Quorum Consensus, the individual user will automatically vote Yes, No or Abstain for a given project. In order for a Yes or No vote to happen, a Quorum must have an active participation of at least `m` (Quorum Participation Threshold) of the members towards that given project (eg. 2/3 of the members did actively vote yes/no rather than abstaining).
If the Quorum Participation Threshold is met, then the Vote Decision for the individual user is going to be the simple majority of the Quorum Members decisions. If there's no majority, then the decision is to abstain.
Some examples can be listed as follows for a Quorum Participation Threshold of 2/3 and Quorum Size of 5:
- If all 5 Quorum members vote:
- 3 Yes, 2 No → Delegating User automatically votes Yes
- 2 Yes, 3 No → Delegating User automatically votes No
- If 4 Quorum members vote, while one abstains:
- 3 Yes, 1 No → Delegating User automatically votes Yes
- 2 Yes, 2 No → No absolute majority, Delegating User automatically Abstains.
- If 3 Quorum Members vote, while two abstain:
- Quorum Participation Threshold is not met, Delegating User automatically Abstains
#### Example Implementation in Python
```python
def query_user_quorum(user_id: UserUUID, max_quorum_size: int=5) -> list[UserUUID]:
"""
Retrieves the Actual Quorum for a given delegating user.
Returns `None` if the User is not delegating.
"""
(action, payload) = USER_ACTIONS[user_id]
actual_quorum = None
if action == Action.Delegate:
actual_quorum = []
current_delegatees = payload
for delegatee in current_delegatees:
delegatee_action = USER_ACTIONS.get(delegatee, None)
if delegatee_action is None:
continue
else:
action = delegatee_action[0]
if action == Action.RoundVote:
actual_quorum.append(delegatee)
if len(actual_quorum) == max_quorum_size:
return actual_quorum
return actual_quorum
else:
return None
def quorum_participation(quorum: list[UserUUID], project_id: ProjectUUID, quorum_size: int=5) -> float:
"""
Active participation share of a quorum towards a project.
"""
active_delegatees = 0
for delegatee in quorum:
if USER_ACTIONS[delegatee][1].get(project_id, None) is not None:
active_delegatees += 1
return active_delegatees / quorum_size
def quorum_agreement(quorum: list[UserUUID], project_id: ProjectUUID, initial_agreement: float=0.0) -> float:
"""
Compute the quorum agreement for the active participants
"""
agreement = initial_agreement
quorum_size = 0
for delegatee in quorum:
delegatee_action = USER_ACTIONS.get(delegatee, None)
action = USER_ACTIONS[delegatee][1].get(project_id, None)
# Note: this logic would be different if we were to weight by User Voting Power
if action is not None:
quorum_size += 1
if action is Vote.Yes:
agreement += 1
elif action is Vote.No:
agreement += -1
else:
agreement += 0
return agreement / quorum_size
def query_user_vote(user_id, project_id) -> Vote | Action:
(action, payload) = USER_ACTIONS.get(user_id, (None, None))
if action is None:
return Vote.Abstain
if action is Action.RoundVote:
project_vote = payload.get(project_id, None)
if project_vote is None:
return Vote.Abstain
else:
return project_vote
if action is Action.Delegate:
return Action.Delegate
def quorum_delegate_result(user_id, project_id, participation_threshold=0.66, agreement_threshold=0.5):
"""
Oracle Module for the Quorum Delegation Neuron.
"""
vote = query_user_vote(user_id, project_id)
if vote is Action.Delegate:
quorum = query_user_quorum(user_id)
if quorum_participation(quorum, project_id) > participation_threshold:
agreement = quorum_agreement(quorum, project_id)
if agreement > agreement_threshold:
return Vote.Yes
elif agreement < -agreement_threshold:
return Vote.No
else:
return Vote.Abstain
else:
return Vote.Abstain
else:
return vote
```
#### Resources
* [SCF Voting Mechanisms – Decisions for the PoC](https://hackmd.io/@blockscience/stellar-poc-decisions)
* [SCF Voting Mechanism PoC Design](https://hackmd.io/HzRrf1NtQ_a7nlSvX_stXg)
* [SCF Trust Bonus](https://hackmd.io/@blockscience/sdf-trust-bonus)
* [External Notes](https://docs.google.com/document/d/12V2Z4zT-rtd_oDJ8Ra-xN25h7HQyVQJXDaWBw6BhutA/edit#heading=h.1tlh3k3l5n6q)
* [Ideation Report](https://docs.google.com/document/d/1heCWpDVLm0NkhDWKhgLhpPhSgGZTVCPc7PcMQkJrDpA/edit#heading=h.jd1juvrmxwtg)
* [Lucidchart](https://lucid.app/lucidchart/3233754a-d598-422a-9b91-38a4122d1ed7/edit?viewport_loc=20%2C-3886%2C8626%2C10225%2CnhW0MXQ7lP.Y&invitationId=inv_c6d4cbe3-0807-4c14-960a-0289f7699022)
## Implementation Instructions
**Authors:** BlockScience and SDF, July 2023
A PoC implementation in Rust for Soroban by Alejo Mendoza, Karol Bisztyga, and Mateusz Kowalski is located in the voting-poc GitHub repository. It implements the Neural Governance, Quorum Delegation, and the Trust Graph Bonus governance modules and we'll use it as the basis for providing instructions.
A technical summary for learnings when implementing an MVP version of Neural Quorum Governance on SCF can be found on a post by Karol Bisztyga: [SCF Voting Mechanism Implementation](https://github.com).
#### 1) Decide on Quorum Delegation Parameters
[GitHub Link](https://github.com/alejomendoza/voting-poc/blob/84611b625a607bd26d0db5b6f5efe125af769f0d/src/voting_system/src/types.rs#L7)
```rust
pub const QUORUM_SIZE: u32 = 5;
pub const QUORUM_PARTICIPATION_TRESHOLD: u32 = 3;
pub const MIN_DELEGATEES: u32 = 5;
pub const MAX_DELEGATEES: u32 = 10;
```
#### 2) Implement Base Module
An example for instructions to set this up can be found [here](https://github.com/alejomendoza/voting-poc/tree/main#setting-up).
## Tuning Guidelines
**Authors:** BlockScience and SDF, July 2023
Quorum Delegation can be tuned to capture phenomena within a community. Besides parametrizing, additional logical adjustments can be made.
#### Tuning Story 1: When to change the quorum sizes (e.g., max quorum candidates or minimum quorum size)
##### Candidate Pool:
The quorum candidates are limited by the maximum amount of other users that might be taken as input for a quorum. Setting this value lower requires users to put more thought into their choice, but might result in insufficient active voters to successfully come to a quorum agreement. Setting this value higher allows users to make a wider selection of candidates, but could result in unintended outcomes for the user if their primary candidates do not actively vote, leaving only back-up candidates for the quorum.
##### Quorum Size:
Allowing lower quorum sizes can increase flexibility for users, such as allowing for traditionally known delegation with only 1 other user being delegated to. However, low quorum sizes can increase the likelihood of users accidentally abstaining due to their delegates not voting. Setting a higher minimum requires users to choose more candidates, which decreases the effect of individual delegates. At the same time, this increases the cost of time for users to delegate.
##### Candidates vs Quorum Size:
Setting the maximum amount of candidates equal to the quorum size increases the risk of a user accidentally abstaining, as no further candidates can be drawn from to form a successful quorum. Increasing the gap between candidates and quorum size allows for a higher likelihood of a successful quorum, while also increasing the risk of less representative voting outcomes if backup candidates do not reflect a user's opinion as accurately.
#### Tuning Story 2: When to change the absolute / relative agreement thresholds?
A quorum comes to an agreement (which results in a user voting with their choice) only when a certain number of quorum members actively vote, and when these quorum members reach a set agreement threshold.
##### Setting the minimum number of actively voting quorum members:
Lowering the fraction of required active votes increases the likelihood of a successful delegation vote, while also increasing the potential effects of individual delegates. Raising the fraction of required active votes decreases the likelihood of a successful delegation vote, while also decreasing the potential effects of individual delegates.
##### Setting the absolute agreement threshold:
A community might face a decision where the expected impact is highly specialized. Increasing the absolute majority threshold can result in quorum votes to only be counted when quorums are in high agreement, while a larger band is allocated for non-voting due to missing agreement.
#### Tuning Story 3: When to disable/enable re-delegation?
Re-delegation of votes — as in User A delegates to User B, while User B delegates to User C — can enable more complex voting scenarios to be represented. As an example, a community might face a decision that only a smaller subset is truly knowledgeable about, while this subset is not widely known. Allowing for redelegation could channel voting decisions through this community, until it reaches those who are more certain about their vote. However, this increases the risk of many users abstaining, as circular delegation can result in quorums not reaching their agreement thresholds.
#### Tuning Story 4: What to do if there are more than yes/no/abstain actions?
Currently, Quorum Delegation represents three choices — yes, no, abstain. However, certain decisions require more nuanced representation and might require voters to pick a parameter value that they see as most representative of their opinion. In such a case, Quorum Voting could help to aggregate values picked by a representative sample chosen by the user. In this scenario, QD would need to be tuned to accept values, aggregate them, then provide as output a direct vote by the user on the aggregated value.
#### Tuning Story 5: Deciding internal Quorum Votes through vote or voting power?
In the reference PoC implementation, a quorum's outcome is decided by individual votes of delegates. This puts equal importance on each quorum member. Alternatively, one might decide that the voting power of each member should factor into the internal vote decision, or that users could set individual weights for quorum candidates (which can be somewhat similarly enabled through allowing users to set the same candidate more than once for their quorum).
## Simulations
#### cadCAD Demo
You can find a demo for cadCAD here: [cadCAD Demo](https://github.com/BlockScience/gov-modules-demos/blob/main/demos/quorum_delegation.ipynb)
#### Visualizations

