# How claim disputes work on chain

> How the IXO Protocol's Claims module handles a dispute: who can file one, the stake, performance deposits, adjudication, and how the penalty is paid.

The Claims module of the IXO Protocol lets anyone challenge the party who submitted a claim, or the
party who evaluated it. A dispute is its own record on chain, and both sides have money at risk: the
disputer stakes a deposit, and the agents who submit and evaluate hold performance deposits that
they lose if a dispute goes against them. An adjudicator approved by the collection decides the
outcome.

This page describes the mechanism as the Claims module implements it in release v8.0.0 of
[`ixo-blockchain`](https://github.com/ixofoundation/ixo-blockchain/tree/main/x/claims). The module's
own specification is in its
[`spec` folder](https://github.com/ixofoundation/ixo-blockchain/tree/main/x/claims/spec).

<Info>
  Disputes are configured for each claim collection. Before you rely on a dispute process, read the
  collection's configuration: its dispute deposit, its adjudicators and its required performance
  deposits. A collection with no adjudicators has nobody who can decide a dispute.
</Info>

## The parts

| Part | What it is |
| --- | --- |
| Dispute | A record against one claim and one role. Its status is `OPEN`, `AWARDED` or `DISMISSED`. |
| Target role | The party being challenged: the `SUBMITTER` of the claim, or its `EVALUATOR`. |
| Dispute deposit | The stake the disputer locks when filing. The collection sets the amount. |
| Performance deposit | A balance an agent keeps on a collection. The collection can require one before an agent may submit or evaluate. It is what a penalty is taken from. |
| Adjudicator | A DID on the collection's list of approved adjudicators. Each one sets its own reward percentage. |
| Penalty | What the losing agent pays when a dispute is awarded. The collection fixes it, or the adjudicator sets it within a cap. |

```mermaid
flowchart LR
  F["Filed: OPEN"] --> A["Adjudicator decides"]
  A --> W["AWARDED"]
  A --> D["DISMISSED"]
```

## Who can file a dispute

Anyone with a registered DID can file one. The signing account must be a key on that DID. No role
on the collection is needed, and no grant.

What stops frivolous disputes is the stake. The disputer must lock the collection's dispute deposit
when filing, and loses it if the dispute is dismissed.

## What a dispute targets

A dispute is against one claim and exactly one role:

- **`SUBMITTER`.** The agent that submitted the claim.
- **`EVALUATOR`.** The agent that evaluated it. The claim must have an evaluation.

To challenge both, file two disputes.

A `flagged` evaluation cannot be disputed. Flagged means the evaluator declined to make a final
call, and the way forward is for an evaluator to re-evaluate the claim to a final status. That
evaluation can then be disputed.

## What filing does

A dispute is filed with `MsgDisputeClaim`. It carries the claim's id, the target role, and a pointer
to your evidence: a URI, its media type, its content hash, and whether it is encrypted. The content
hash is the dispute's key, so it must be unique.

When the message is accepted:

1. The dispute deposit moves from the disputer's account into the collection's escrow account. The
   amount is recorded on the dispute, so a later change to the collection's setting does not change
   what is at stake.
2. The dispute is stored with status `OPEN`.
3. The targeted agent is blocked on that collection. They cannot submit or evaluate claims there,
   and they cannot withdraw their performance deposit, until the dispute is decided.

Only one `OPEN` dispute can exist for a claim and a role. If a dispute is awarded, no further
dispute can be filed against that claim and role. If it is dismissed, a new one can be filed, for
example with new evidence.

Illustrative example, in the pattern of the IXO MultiClient SDK (`@ixo/impactxclient-sdk`):

```ts
const disputeMsg = {
  typeUrl: "/ixo.claims.v1beta1.MsgDisputeClaim",
  value: ixo.claims.v1beta1.MsgDisputeClaim.fromPartial({
    subjectId: "bafkreih...", // the id of the claim
    agentDid: "did:ixo:...", // your DID
    agentAddress: signerAddress, // must be a key on that DID
    disputeType: 1, // your own code for the kind of dispute
    targetRole: ixo.claims.v1beta1.DisputeTargetRole.DISPUTE_TARGET_ROLE_EVALUATOR,
    data: ixo.claims.v1beta1.DisputeData.fromPartial({
      uri: "ipfs://...", // where your evidence document is
      type: "application/json",
      proof: "bafkrei...", // its content hash, unique to this dispute
      encrypted: false,
    }),
  }),
};

await signingClient.signAndBroadcast(signerAddress, [disputeMsg], "auto");
```

## Performance deposits

A collection can require the agents who work on it to keep a deposit there. It sets one amount for
agents who submit claims and one for agents who evaluate them. An agent whose balance is below the
required amount cannot submit or evaluate.

- An agent adds to their own balance with `MsgAddPerformanceDeposit`. The money moves into the
  collection's escrow account. An agent can top up even while a dispute is open against them.
- An agent takes money back with `MsgWithdrawPerformanceDeposit`. It is refused while any `OPEN`
  dispute targets the agent on that collection.
- A collection can set a minimum deposit period. Each top-up locks the balance for that long, so an
  agent cannot deposit, act and withdraw in one go.

## Adjudication

An adjudicator settles an `OPEN` dispute with `MsgAdjudicateDispute`. The adjudicator's DID must be
on the collection's list, and the signing account must be a key on that DID. The chain does not
assign a dispute to a particular adjudicator: any adjudicator on the list can decide it.

The adjudicator gives an outcome, `AWARDED` or `DISMISSED`, and can attach a document that sets out
their reasoning, in the same form as the disputer's evidence.

For an award, the penalty comes from one of two places:

- If the collection has a fixed penalty for each dispute, that amount is used.
- If it has none, the adjudicator must give an amount. It cannot be more than the deposit the
  collection requires for the losing agent's role.

## Who is paid

The adjudicator's share is their own reward percentage, as listed on the collection. The winner
gets the rest.

| Outcome | Who loses | Where the money comes from | Where it goes |
| --- | --- | --- | --- |
| `AWARDED` | The targeted agent | The penalty, taken from the agent's performance deposit | The adjudicator's share to the adjudicator, the rest to the disputer. The disputer also gets their dispute deposit back in full |
| `DISMISSED` | The disputer | The disputer's dispute deposit | The adjudicator's share to the adjudicator, the rest to the targeted agent |

Two limits apply:

- A penalty cannot be more than the agent's deposit balance at that moment. If an earlier dispute
  has already reduced the balance, less is paid. The resolution records both the intended penalty
  and the amount actually paid.
- The adjudicator's share is rounded down, and the winner receives the remainder.

If the adjudicator's DID belongs to an entity, their share is paid to that entity's adjudicator
revenue account. Otherwise it is paid to the signing account.

An example. A collection sets a dispute deposit of 10, a fixed penalty of 50, and lists an
adjudicator with a reward of 20 percent.

- Awarded: 50 is taken from the agent's performance deposit. The adjudicator receives 10 and the
  disputer receives 40, plus their own 10 back.
- Dismissed: the disputer's 10 is the pot. The adjudicator receives 2 and the targeted agent
  receives 8.

## What a dispute does not do

- It does not change the claim's evaluation. An approved claim stays approved. The dispute and its
  resolution are separate records.
- It does not reverse a payment that was made when the claim was evaluated. The penalty comes from
  the losing agent's performance deposit.
- It does not use the evaluation status `disputed`. New evaluations can no longer be given that
  status, and the chain refuses one that tries. Older claims that carry it keep it as history.

## Configure disputes on a collection

The collection's admin sets the configuration with `MsgUpdateCollectionDisputeConfig`. Every field
replaces the current value, so send the whole configuration each time.

| Setting | What it controls |
| --- | --- |
| `serviceAgentDepositRequired` | The performance deposit an agent needs before they can submit a claim. Empty means none |
| `evaluatorDepositRequired` | The same, for agents who evaluate |
| `disputeDepositAmount` | The stake a disputer locks when filing |
| `adjudicators` | The approved adjudicators, each with a DID and a reward percentage from 0 to 100 |
| `penaltyAmountPerDispute` | A fixed penalty for an awarded dispute. Empty lets the adjudicator set it |
| `minDepositPeriod` | How long a performance deposit stays locked after each top-up |

The chain enforces these rules:

- If any deposit, penalty or dispute stake is set, the list of adjudicators cannot be empty.
- A fixed penalty cannot be more than either required performance deposit.
- The list of adjudicators cannot be cleared while a dispute is open.
- A dispute that is already open keeps the deposit recorded when it was filed. The adjudicator list
  and the penalty rules are read when the dispute is decided.

<Warning>
  Set a dispute deposit and at least one adjudicator together. If the dispute deposit is empty,
  filing costs the disputer nothing, and the targeted agent stays blocked until an adjudicator is on
  the list and decides.
</Warning>

## Messages

| Message | Signed by | Purpose |
| --- | --- | --- |
| `/ixo.claims.v1beta1.MsgDisputeClaim` | The disputer | File a dispute and lock the stake |
| `/ixo.claims.v1beta1.MsgAdjudicateDispute` | An approved adjudicator | Decide an open dispute |
| `/ixo.claims.v1beta1.MsgUpdateCollectionDisputeConfig` | The collection's admin | Set deposits, adjudicators and penalty |
| `/ixo.claims.v1beta1.MsgAddPerformanceDeposit` | The agent | Add to their deposit on a collection |
| `/ixo.claims.v1beta1.MsgWithdrawPerformanceDeposit` | The agent | Take back some or all of their deposit |

Version 3.1.0 of the IXO MultiClient SDK includes these messages.

## Read disputes and deposits

These are the REST paths of the Claims module's queries.

| Query | Path |
| --- | --- |
| One dispute, by its content hash | `/ixo/claims/dispute/{proof}` |
| All disputes | `/ixo/claims/disputes` |
| The current dispute for a claim and a role | `/ixo/claims/dispute-by-subject/{subjectId}/{targetRole}` |
| Every dispute for a claim | `/ixo/claims/disputes-for-subject/{subjectId}` |
| One agent's deposit on a collection | `/ixo/claims/collection/{collectionId}/deposit/{agentAddress}` |
| All deposits on a collection | `/ixo/claims/collection/{collectionId}/deposits` |
| A collection, with its dispute settings and counters | `/ixo/claims/collection/{id}` |

A collection counts its open, awarded and dismissed disputes. Filing emits `ClaimDisputedEvent`, and
a decision emits `DisputeResolvedEvent`. Both carry the full dispute record.

## Errors you may see

| Error | Meaning |
| --- | --- |
| `dispute with proof already exists` | The content hash of your evidence has been used for another dispute |
| `cannot dispute evaluator role: claim has not been evaluated` | You targeted the evaluator of a claim with no evaluation |
| `cannot dispute a FLAGGED evaluation` | The evaluation is not final. It must be re-evaluated first |
| `an OPEN dispute already exists for this (subject_id, target_role)` | Wait for the open dispute to be decided |
| `this (subject_id, target_role) was previously AWARDED` | No further dispute is allowed against that claim and role |
| `collection has no adjudicators configured` | The collection takes a dispute deposit and has nobody to decide disputes, so the chain will not lock your stake |
| `agent has an active dispute on this collection` | You are the target of an open dispute and cannot submit or evaluate there until it is decided |
| `agent performance-deposit balance is below required amount` | Top up your deposit before you submit or evaluate |
| `performance-deposit cannot be withdrawn while agent has active disputes` | Wait for the dispute to be decided |
| `performance-deposit is still within the minimum deposit period` | Wait for the lock from your last top-up to end |
| `adjudicator_did is not in the collection's adjudicators whitelist` | Only a listed adjudicator can decide |
| `penalty_amount exceeds the loser's deposit-required cap` | Lower the penalty to the required deposit for that role, or less |

## Related

<CardGroup cols={2}>
  <Card title="What each record proves" icon="file-shield" href="/trust-boundaries">
    Where a dispute fits among the other records, and what each one shows.
  </Card>
  <Card title="Claims management" icon="file-signature" href="/guides/dev/ixo-claims">
    Collections, claims and evaluations.
  </Card>
  <Card title="Custom authorisations for IXO Claims" icon="key" href="/guides/dev/authz-custom">
    The grants agents need to submit and evaluate.
  </Card>
  <Card title="Decision oracle" icon="scale-balanced" href="/build-an-oracle/decision-oracle">
    The service that evaluates claims and records decisions.
  </Card>
</CardGroup>
