# Multi-Sig / Voting

> Require weighted quorum voting from multiple parties before transfers can proceed (multi-sig, governance, etc.)

**Category:** Approval Patterns

## Summary

Enables multi-signature-like approval via votingChallenges\[] in approvalCriteria.

* Each voter has an address and a weight
* quorumThreshold: percentage (0-100) of total possible weight that must vote "yes"
* Voters cast votes via MsgCastVote with yesWeight (0-100%)
* Non-voting voters count as 0% yes; threshold is % of ALL voters' total weight, not just those who voted
* Votes can be updated (re-casting overwrites previous vote)
* proposalId: unique identifier — changing it resets the vote tracker
* v29: resetAfterExecution (bool) — automatically resets all votes after quorum is met and transfer executes, enabling recurring multi-sig
* v29: delayAfterQuorum (Uint, ms) — enforces a waiting period after quorum before the transfer can execute (e.g., timelock)
* Common patterns: unanimous (threshold: "100", equal weights), majority (threshold: "51"), weighted governance

## Instructions

## Multi-Sig / Voting Challenges

### Concept

Voting challenges enable multi-signature-like approval where multiple parties must vote before a transfer can proceed. Each voter has a configurable weight, and a quorum threshold (percentage of total weight) must be met. This is configured via the `votingChallenges[]` field in `approvalCriteria`.

### How It Works

1. An approval has `votingChallenges` with a list of voters, weights, and a quorum threshold
2. Voters cast votes using `MsgCastVote` with a `yesWeight` (0-100%)
3. When a transfer is attempted, the system checks if the quorum threshold is met
4. Threshold is calculated as a percentage of **total possible weight** (all voters), not just those who voted
5. Non-voting voters count as 0% yes

### Structure

```json
{
  "votingChallenges": [
    {
      "proposalId": "proposal-1",
      "quorumThreshold": "50",
      "voters": [
        { "address": "bb1alice...", "weight": "100" },
        { "address": "bb1bob...", "weight": "200" },
        { "address": "bb1charlie...", "weight": "50" }
      ],
      "resetAfterExecution": false,
      "delayAfterQuorum": "0",
      "uri": "",
      "customData": ""
    }
  ]
}
```

### Key Fields

* **proposalId**: Unique identifier for tracking votes. Changing it resets the vote tracker.
* **quorumThreshold**: Percentage (0-100) of total possible weight that must vote "yes"
* **voters**: List of voter addresses with their weights
* **yesWeight** (in MsgCastVote): Percentage (0-100%) allocated to "yes"; remainder goes to "no"
* **resetAfterExecution** (v29): If true, all votes are automatically reset after the quorum is met and the transfer executes. Enables recurring multi-sig without rotating proposalIds.
* **delayAfterQuorum** (v29): Delay in milliseconds after quorum is reached before the transfer can execute. Acts as a timelock — gives voters time to change their vote or raise objections.

### Common Patterns

#### Multi-Sig (Unanimous)

All parties must approve. Set `quorumThreshold: "100"` with equal weights:

```json
{ "quorumThreshold": "100", "voters": [
  { "address": "bb1a...", "weight": "1" },
  { "address": "bb1b...", "weight": "1" },
  { "address": "bb1c...", "weight": "1" }
]}
```

#### Majority Vote

Require >50% approval:

```json
{ "quorumThreshold": "51", "voters": [
  { "address": "bb1a...", "weight": "1" },
  { "address": "bb1b...", "weight": "1" },
  { "address": "bb1c...", "weight": "1" }
]}
```

#### Weighted Governance

Different stakeholders have different voting power:

```json
{ "quorumThreshold": "66", "voters": [
  { "address": "bb1founder...", "weight": "1000" },
  { "address": "bb1investor...", "weight": "500" },
  { "address": "bb1community...", "weight": "100" }
]}
```

### Threshold Calculation Example

* Voter A: weight 100, votes 100% yes → contributes 100
* Voter B: weight 200, votes 50% yes → contributes 100
* Voter C: weight 50, doesn't vote → contributes 0
* Total possible weight: 350
* Total yes weight: 200
* Percentage: (200 × 100) / 350 = 57.14%
* If quorumThreshold is 50 → **satisfied**

### Important Notes

* Votes are cast via `MsgCastVote` — a separate transaction from the transfer itself
* Votes can be updated (re-casting overwrites the previous vote)
* Vote keys are scoped: `collectionId-approverAddress-approvalLevel-approvalId-proposalId-voterAddress`
* Set realistic thresholds — high thresholds with many voters may be hard to meet if voters abstain
* **Vote reset behavior** — By default, vote state does not reset after quorum is met (one-time transfers). With v29's `resetAfterExecution: true`, votes are automatically cleared after a successful transfer, enabling recurring multi-sig workflows without rotating proposalIds. Use `delayAfterQuorum` to add a timelock between quorum and execution.
* For full documentation, see the BitBadges docs on voting challenges


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.bitbadges.io/token-standard/skills/multi-sig-voting.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
