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. The module's
own specification is in its
spec folder.
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.
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. |
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:
- 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.
- The dispute is stored with status
OPEN. - 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):
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 anyOPENdispute 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.
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.
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 |