> For the complete documentation index, see [llms.txt](https://socious.gitbook.io/midnight-smart-contract-library/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://socious.gitbook.io/midnight-smart-contract-library/dispute-resolution/dispute-resolution-smart-contract.md).

# Dispute Resolution Smart Contract

## Tutorial: Decentralized Dispute Resolution

Code: <https://github.com/socious-io/midnight-escrow/>

{% embed url="<https://youtu.be/b0Q-eg3cncs>" %}

### 1. Overview

The Dispute Resolution mechanism is a core feature of the Midnight Escrow contract. It replaces the need for a centralized arbitrator with a decentralized panel of three voters. If the **Sender** and **Contributor** cannot agree on the completion of work, the **Voters** step in to cast binding votes on whether to release funds or refund them.

### 2. Roles & Permissions

To use this feature, you must understand the specific roles involved:

* **Voters (3):** Independent parties whose public keys are registered when the escrow is created. They are the only ones permitted to call the `vote` circuit.
* **Admin:** The entity authorized to trigger the final settlement (`dispute` circuit) once a consensus is reached.
* **Sender/Contributor:** The disputing parties. They cannot cast votes.

### 3. The Resolution Workflow

#### Step 1: Initial Setup (During Creation)

When creating an escrow, you must initialize the voter slots. While the current contract version in the repository initializes them with a placeholder (the contributor's key) or flags them as empty (`voter*_set: 0`), a robust deployment should assign these to independent third parties.

**Code Reference (`create` circuit):**

```
// In a production scenario, these would be distinct keys
voter1: disclose(voterKey1),
voter2: disclose(voterKey2),
voter3: disclose(voterKey3),
```

#### Step 2: Casting Votes

When a dispute arises, each of the three voters must examine the evidence (off-chain) and cast their vote on-chain using the `vote` circuit.

**Function Signature:**

```
export circuit vote(
    id: Uint<32>,
    voter: ZswapCoinPublicKey,
    vote_choice: ESCROW_VOTE
): []
```

**Parameters:**

* **`id`**: The unique ID of the escrow in dispute.
* **`voter`**: The public key of the voter (must match one of the registered `voter1`, `voter2`, or `voter3`).
* **`vote_choice`**: An Enum value indicating the decision.
  * `1`: **Release** (Pay the Contributor)
  * `2`: **Refund** (Return funds to Sender)

**Example Usage (TypeScript/JS SDK):**

```
// Example: Voter 1 votes to RELEASE funds
await contract.vote(
  escrowId,
  voter1PublicKey,
  1 // Enum value for ESCROW_VOTE.release
);
```

#### Step 3: Reaching Consensus

The contract automatically tracks the tally in the state variables `release_votes` and `refund_votes`.

* **Threshold:** A simple majority of **2 out of 3** votes is required.
* **Automatic Trigger:** The current smart contract logic checks the vote count immediately after a vote is cast. If the threshold (2 votes) is met, the contract **automatically** executes the transfer.
  * *Note:* In older versions, a separate `dispute` call was required. In the `Secure Decentralized Escrow Contract with Voting` version, the `vote` circuit explicitly calls `release` or `refund` internally if the count `>= 2`.

#### Step 4: Final Settlement (The `dispute` Circuit)

*Note: This step is technically redundant in the latest "Voting" contract version because the `vote` function handles execution, but the `dispute` function remains available as a manual trigger if needed by the Admin.*

**Function Signature:**

```
export circuit dispute(
    id: Uint<32>,
    admin: ZswapCoinPublicKey
): []
```

**Usage:** If for some reason the automatic trigger fails or creates a state where funds didn't move (but votes are recorded), the Admin can call this function to force the settlement based on the existing vote count.

**Logic:**

```
if (e.release_votes >= 2) {
    release(disclose(id), e.admin);
} else if (e.refund_votes >= 2) {
    refund(disclose(id), e.admin);
}
```

### 4. Troubleshooting

#### "Voter already voted" Error

* **Cause:** The specific voter address attempting to transact has already cast a vote for this Escrow ID.
* **Fix:** Votes are immutable. Once cast, a voter cannot change their decision.

#### "No majority to resolve dispute" Error

* **Cause:** The Admin called the `dispute` function, but the vote tally is currently split (e.g., 1 Release, 0 Refund) or insufficient.
* **Fix:** Wait for the remaining voters to cast their votes using the `vote` circuit.

#### "Voter must differ from sender and contributor" Error

* **Cause:** The wallet attempting to vote matches the Sender or Contributor's public key.
* **Fix:** Ensure you are using the specific wallet associated with the designated Voter ID.
