Skip to content

Commit 4954a10

Browse files
authored
Merge pull request #1839 from kleros/fix/use-answer-id-in-voting
Fix/use answer id in voting
2 parents 7af5a24 + af70b25 commit 4954a10

24 files changed

+183
-158
lines changed

kleros-sdk/src/dataMappings/utils/disputeDetailsSchema.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,19 @@ export enum QuestionType {
3333
export const QuestionTypeSchema = z.nativeEnum(QuestionType);
3434

3535
export const AnswerSchema = z.object({
36-
id: z
37-
.string()
38-
.regex(/^0x[0-9a-fA-F]+$/)
39-
.optional(),
36+
id: z.string().regex(/^0x[0-9a-fA-F]+$/),
4037
title: z.string(),
4138
description: z.string(),
4239
reserved: z.boolean().optional(),
4340
});
4441

42+
export const RefuseToArbitrateAnswer = {
43+
id: "0x0",
44+
title: "Refuse to Arbitrate / Invalid",
45+
description: "Refuse to Arbitrate / Invalid",
46+
reserved: true,
47+
};
48+
4549
export const AttachmentSchema = z.object({
4650
label: z.string(),
4751
uri: z.string(),

kleros-sdk/src/dataMappings/utils/populateTemplate.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import mustache from "mustache";
22
import { DisputeDetails } from "./disputeDetailsTypes";
3-
import DisputeDetailsSchema from "./disputeDetailsSchema";
3+
import DisputeDetailsSchema, { RefuseToArbitrateAnswer } from "./disputeDetailsSchema";
44

55
export const populateTemplate = (mustacheTemplate: string, data: any): DisputeDetails => {
66
const render = mustache.render(mustacheTemplate, data);
@@ -11,5 +11,11 @@ export const populateTemplate = (mustacheTemplate: string, data: any): DisputeDe
1111
throw validation.error;
1212
}
1313

14+
// Filter out any existing answer with id 0 and add our standard Refuse to Arbitrate option
15+
(dispute as DisputeDetails).answers = [
16+
RefuseToArbitrateAnswer,
17+
...((dispute as DisputeDetails).answers.filter((answer) => answer.id && BigInt(answer.id) !== BigInt(0)) || []),
18+
];
19+
1420
return dispute;
1521
};

kleros-sdk/src/utils/getDispute.ts

-11
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,5 @@ export const getDispute = async (disputeParameters: GetDisputeParameters): Promi
5656

5757
const populatedTemplate = populateTemplate(templateData, data);
5858

59-
// Filter out any existing answer with id 0 and add our standard Refuse to Arbitrate option
60-
populatedTemplate.answers = [
61-
{
62-
id: "0x0",
63-
title: "Refuse to Arbitrate / Invalid",
64-
description: "Refuse to Arbitrate / Invalid",
65-
reserved: true,
66-
},
67-
...(populatedTemplate.answers?.filter((answer) => answer.id && Number(answer.id) !== 0) || []),
68-
];
69-
7059
return populatedTemplate;
7160
};

subgraph/core/schema.graphql

+11-2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ interface Evidence {
6363
fileTypeExtension: String
6464
}
6565

66+
6667
############
6768
# Entities #
6869
############
@@ -267,17 +268,25 @@ type ClassicDispute implements DisputeKitDispute @entity {
267268
extraData: Bytes!
268269
}
269270

271+
type Answer @entity {
272+
id: ID! # classicRound.id-answerId
273+
answerId: BigInt!
274+
count: BigInt!
275+
paidFee: BigInt!
276+
funded: Boolean!
277+
localRound: ClassicRound!
278+
}
279+
270280
type ClassicRound implements DisputeKitRound @entity {
271281
id: ID! # disputeKit.id-coreDispute-dispute.rounds.length
272282
localDispute: DisputeKitDispute!
273283
votes: [Vote!]! @derivedFrom(field: "localRound")
284+
answers: [Answer!]! @derivedFrom(field: "localRound")
274285

275286
winningChoice: BigInt!
276-
counts: [BigInt!]!
277287
tied: Boolean!
278288
totalVoted: BigInt!
279289
totalCommited: BigInt!
280-
paidFees: [BigInt!]!
281290
contributions: [ClassicContribution!]! @derivedFrom(field: "localRound")
282291
feeRewards: BigInt!
283292
totalFeeDispersed: BigInt!

subgraph/core/src/DisputeKitClassic.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { ensureClassicContributionFromEvent } from "./entities/ClassicContributi
1414
import { createClassicDisputeFromEvent } from "./entities/ClassicDispute";
1515
import {
1616
createClassicRound,
17+
ensureAnswer,
1718
updateChoiceFundingFromContributionEvent,
1819
updateCountsAndGetCurrentRuling,
1920
} from "./entities/ClassicRound";
@@ -101,11 +102,16 @@ export function handleChoiceFunded(event: ChoiceFunded): void {
101102
const localRound = ClassicRound.load(roundID);
102103
if (!localRound) return;
103104

105+
const answer = ensureAnswer(roundID, choice);
106+
104107
const currentFeeRewards = localRound.feeRewards;
105-
const deltaFeeRewards = localRound.paidFees[choice.toI32()];
108+
const deltaFeeRewards = answer.paidFee;
106109
localRound.feeRewards = currentFeeRewards.plus(deltaFeeRewards);
107110
localRound.fundedChoices = localRound.fundedChoices.concat([choice]);
108111

112+
answer.funded = true;
113+
answer.save();
114+
109115
if (localRound.fundedChoices.length > 1) {
110116
const disputeKitClassic = DisputeKitClassic.bind(event.address);
111117
const klerosCore = KlerosCore.bind(disputeKitClassic.core());

subgraph/core/src/entities/ClassicContribution.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { ClassicContribution } from "../../generated/schema";
22
import { Contribution as ContributionEvent, Withdrawal } from "../../generated/DisputeKitClassic/DisputeKitClassic";
33
import { DISPUTEKIT_ID } from "../DisputeKitClassic";
4+
import { ensureUser } from "./User";
45

56
export function ensureClassicContributionFromEvent<T>(event: T): ClassicContribution | null {
67
if (!(event instanceof ContributionEvent) && !(event instanceof Withdrawal)) return null;
78
const coreDisputeID = event.params._coreDisputeID.toString();
89
const coreRoundIndex = event.params._coreRoundID.toString();
910
const roundID = `${DISPUTEKIT_ID}-${coreDisputeID}-${coreRoundIndex}`;
11+
ensureUser(event.params._contributor.toHexString());
1012
const contributor = event.params._contributor.toHexString();
1113
const choice = event.params._choice;
1214

+30-27
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import { BigInt } from "@graphprotocol/graph-ts";
22
import { Contribution } from "../../generated/DisputeKitClassic/DisputeKitClassic";
3-
import { ClassicRound } from "../../generated/schema";
4-
import { ONE, ZERO } from "../utils";
3+
import { Answer, ClassicRound } from "../../generated/schema";
4+
import { ZERO } from "../utils";
55

66
export function createClassicRound(disputeID: string, numberOfChoices: BigInt, roundIndex: BigInt): void {
7-
const choicesLength = numberOfChoices.plus(ONE);
87
const localDisputeID = `1-${disputeID}`;
98
const id = `${localDisputeID}-${roundIndex.toString()}`;
109
const classicRound = new ClassicRound(id);
1110
classicRound.localDispute = localDisputeID;
1211
classicRound.winningChoice = ZERO;
13-
classicRound.counts = new Array<BigInt>(choicesLength.toI32()).fill(ZERO);
1412
classicRound.tied = true;
1513
classicRound.totalVoted = ZERO;
1614
classicRound.totalCommited = ZERO;
17-
classicRound.paidFees = new Array<BigInt>(choicesLength.toI32()).fill(ZERO);
1815
classicRound.feeRewards = ZERO;
1916
classicRound.appealFeesDispersed = false;
2017
classicRound.totalFeeDispersed = ZERO;
@@ -27,21 +24,31 @@ class CurrentRulingInfo {
2724
tied: boolean;
2825
}
2926

27+
export function ensureAnswer(localRoundId: string, answerId: BigInt): Answer {
28+
const id = `${localRoundId}-${answerId}`;
29+
let answer = Answer.load(id);
30+
if (answer) return answer;
31+
answer = new Answer(id);
32+
answer.answerId = answerId;
33+
answer.count = ZERO;
34+
answer.paidFee = ZERO;
35+
answer.funded = false;
36+
answer.localRound = localRoundId;
37+
return answer;
38+
}
39+
3040
export function updateCountsAndGetCurrentRuling(id: string, choice: BigInt, delta: BigInt): CurrentRulingInfo {
3141
const round = ClassicRound.load(id);
3242
if (!round) return { ruling: ZERO, tied: false };
33-
const choiceNum = choice.toI32();
34-
const newChoiceCount = round.counts[choiceNum].plus(delta);
35-
let newCounts: BigInt[] = [];
36-
for (let i = 0; i < round.counts.length; i++) {
37-
if (BigInt.fromI32(i).equals(choice)) {
38-
newCounts.push(newChoiceCount);
39-
} else {
40-
newCounts.push(round.counts[i]);
41-
}
42-
}
43-
round.counts = newCounts;
44-
const currentWinningCount = round.counts[round.winningChoice.toI32()];
43+
const answer = ensureAnswer(id, choice);
44+
45+
answer.count = answer.count.plus(delta);
46+
47+
const newChoiceCount = answer.count;
48+
49+
const winningAnswer = ensureAnswer(id, round.winningChoice);
50+
const currentWinningCount = winningAnswer.count;
51+
4552
if (choice.equals(round.winningChoice)) {
4653
if (round.tied) round.tied = false;
4754
} else {
@@ -53,6 +60,8 @@ export function updateCountsAndGetCurrentRuling(id: string, choice: BigInt, delt
5360
}
5461
}
5562
round.totalVoted = round.totalVoted.plus(delta);
63+
64+
answer.save();
5665
round.save();
5766
return { ruling: round.winningChoice, tied: round.tied };
5867
}
@@ -68,15 +77,9 @@ export function updateChoiceFundingFromContributionEvent(event: Contribution): v
6877

6978
const choice = event.params._choice;
7079
const amount = event.params._amount;
71-
const currentPaidFees = classicRound.paidFees[choice.toI32()];
72-
let newPaidFees: BigInt[] = [];
73-
for (let i = 0; i < classicRound.paidFees.length; i++) {
74-
if (BigInt.fromI32(i).equals(choice)) {
75-
newPaidFees.push(currentPaidFees.plus(amount));
76-
} else {
77-
newPaidFees.push(classicRound.paidFees[i]);
78-
}
79-
}
80-
classicRound.paidFees = newPaidFees;
80+
const answer = ensureAnswer(roundID, choice);
81+
answer.paidFee = answer.paidFee.plus(amount);
82+
83+
answer.save();
8184
classicRound.save();
8285
}

subgraph/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@kleros/kleros-v2-subgraph",
3-
"version": "0.10.3",
3+
"version": "0.11.0",
44
"drtVersion": "0.11.0",
55
"license": "MIT",
66
"scripts": {

web/src/components/Verdict/DisputeTimeline.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,18 @@ const useItems = (disputeDetails?: DisputeDetailsQuery, arbitrable?: `0x${string
9292
const dispute = disputeDetails?.dispute;
9393
if (dispute) {
9494
const rulingOverride = dispute.overridden;
95-
const parsedDisputeFinalRuling = parseInt(dispute.currentRuling);
9695
const currentPeriodIndex = Periods[dispute.period];
9796

9897
return localRounds?.reduce<TimelineItems>(
9998
(acc, { winningChoice }, index) => {
100-
const parsedRoundChoice = parseInt(winningChoice);
10199
const isOngoing = index === localRounds.length - 1 && currentPeriodIndex < 3;
102100
const roundTimeline = rounds?.[index].timeline;
103101

104102
const icon = dispute.ruled && !rulingOverride && index === localRounds.length - 1 ? ClosedCaseIcon : "";
105103
const answers = disputeData?.answers;
106104
acc.push({
107105
title: `Jury Decision - Round ${index + 1}`,
108-
party: isOngoing ? "Voting is ongoing" : getVoteChoice(parsedRoundChoice, answers),
106+
party: isOngoing ? "Voting is ongoing" : getVoteChoice(winningChoice, answers),
109107
subtitle: isOngoing
110108
? ""
111109
: `${formatDate(roundTimeline?.[Periods.vote])} / ${
@@ -124,10 +122,10 @@ const useItems = (disputeDetails?: DisputeDetailsQuery, arbitrable?: `0x${string
124122
rightSided: true,
125123
Icon: StyledClosedCircle,
126124
});
127-
} else if (rulingOverride && parsedDisputeFinalRuling !== parsedRoundChoice) {
125+
} else if (rulingOverride && dispute.currentRuling !== winningChoice) {
128126
acc.push({
129127
title: "Won by Appeal",
130-
party: getVoteChoice(parsedDisputeFinalRuling, answers),
128+
party: getVoteChoice(dispute.currentRuling, answers),
131129
subtitle: formatDate(roundTimeline?.[Periods.appeal]),
132130
rightSided: true,
133131
Icon: ClosedCaseIcon,

web/src/hooks/queries/useClassicAppealQuery.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ const classicAppealQuery = graphql(`
2525
localRounds {
2626
... on ClassicRound {
2727
winningChoice
28-
paidFees
28+
answers {
29+
answerId
30+
count
31+
paidFee
32+
funded
33+
}
2934
fundedChoices
3035
appealFeesDispersed
3136
totalFeeDispersed

0 commit comments

Comments
 (0)