Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type User @entity {
shifts: [TokenAndETHShift!]! @derivedFrom(field: "juror")
draws: [Draw!]! @derivedFrom(field: "juror")
activeDisputes: BigInt!
rounds: [Round!]!
disputes: [Dispute!]!
resolvedDisputes: [Dispute!]!
totalResolvedDisputes: BigInt!
Expand Down Expand Up @@ -142,22 +143,29 @@ type Court @entity {

type Dispute @entity {
id: ID!
disputeID: BigInt!
court: Court!
arbitrated: Arbitrable!
period: Period!
ruled: Boolean!
currentRuling: BigInt!
tied: Boolean!
overridden: Boolean!
periodDeadline: BigInt!
periodNotificationIndex: BigInt!
lastPeriodChange: BigInt!
lastPeriodChangeBlockNumber: BigInt!
periodDeadline: BigInt!
rounds: [Round!]! @derivedFrom(field: "dispute")
currentRound: Round!
currentRoundIndex: BigInt!
jurors: [User!]! @derivedFrom(field: "disputes")
shifts: [TokenAndETHShift!]! @derivedFrom(field: "dispute")
disputeKitDispute: DisputeKitDispute @derivedFrom(field: "coreDispute")
disputeKitDispute: [DisputeKitDispute!]! @derivedFrom(field: "coreDispute")
}

type PeriodIndexCounter @entity {
id: String!
counter: BigInt!
}

type Round @entity {
Expand All @@ -166,6 +174,7 @@ type Round @entity {
tokensAtStakePerJuror: BigInt!
totalFeesForJurors: BigInt!
nbVotes: BigInt!
isCurrentRound: Boolean!
repartitions: BigInt!
penalties: BigInt!
drawnJurors: [Draw!]! @derivedFrom(field: "round")
Expand All @@ -179,8 +188,9 @@ type Draw @entity(immutable: true) {
dispute: Dispute!
round: Round!
juror: User!
voteID: BigInt!
voteIDNum: BigInt!
vote: Vote @derivedFrom(field: "draw")
drawNotificationIndex: BigInt
}

type DisputeKit @entity {
Expand Down Expand Up @@ -228,7 +238,6 @@ type ClassicDispute implements DisputeKitDispute @entity {
currentLocalRoundIndex: BigInt!

numberOfChoices: BigInt!
jumped: Boolean!
extraData: Bytes!
}

Expand Down
10 changes: 10 additions & 0 deletions subgraph/src/KlerosCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
NewPeriod,
StakeSet,
TokenAndETHShift as TokenAndETHShiftEvent,
CourtJump,
Ruling,
StakeDelayed,
AcceptedFeeToken,
Expand All @@ -29,6 +30,7 @@ import { Court, Dispute, User } from "../generated/schema";
import { BigInt } from "@graphprotocol/graph-ts";
import { updatePenalty } from "./entities/Penalty";
import { ensureFeeToken } from "./entities/FeeToken";
import { getAndIncrementPeriodCounter } from "./entities/PeriodIndexCounter";

function getPeriodName(index: i32): string {
const periodArray = ["evidence", "commit", "vote", "appeal", "execution"];
Expand Down Expand Up @@ -128,6 +130,7 @@ export function handleNewPeriod(event: NewPeriod): void {
dispute.period = newPeriod;
dispute.lastPeriodChange = event.block.timestamp;
dispute.lastPeriodChangeBlockNumber = event.block.number;
dispute.periodNotificationIndex = getAndIncrementPeriodCounter(newPeriod);
if (newPeriod !== "execution") {
dispute.periodDeadline = event.block.timestamp.plus(court.timesPerPeriod[event.params._period]);
} else {
Expand Down Expand Up @@ -164,6 +167,13 @@ export function handleAppealDecision(event: AppealDecision): void {
createRoundFromRoundInfo(disputeID, newRoundIndex, roundInfo);
}

export function handleCourtJump(event: CourtJump): void {
const dispute = Dispute.load(event.params._disputeID.toString());
if (!dispute) return;
dispute.court = event.params._toCourtID.toString();
dispute.save();
}

export function handleDraw(event: DrawEvent): void {
createDrawFromEvent(event);
const disputeID = event.params._disputeID.toString();
Expand Down
1 change: 0 additions & 1 deletion subgraph/src/entities/ClassicDispute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export function createClassicDisputeFromEvent(event: DisputeCreation): void {
classicDispute.coreDispute = coreDisputeID;
classicDispute.currentLocalRoundIndex = ZERO;
classicDispute.numberOfChoices = event.params._numberOfChoices;
classicDispute.jumped = false;
classicDispute.extraData = event.params._extraData;
classicDispute.save();
}
6 changes: 4 additions & 2 deletions subgraph/src/entities/Dispute.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { KlerosCore, DisputeCreation } from "../../generated/KlerosCore/KlerosCore";
import { Court, Dispute } from "../../generated/schema";
import { ZERO } from "../utils";
import { getAndIncrementPeriodCounter } from "./PeriodIndexCounter";

export function createDisputeFromEvent(event: DisputeCreation): void {
const contract = KlerosCore.bind(event.address);
const disputeID = event.params._disputeID;
const disputeContractState = contract.disputes(disputeID);
const disputeContractState = KlerosCore.bind(event.address).disputes(disputeID);
const dispute = new Dispute(disputeID.toString());
const courtID = disputeContractState.value0.toString();
dispute.court = courtID;
dispute.disputeID = disputeID;
dispute.arbitrated = event.params._arbitrable.toHexString();
dispute.period = "evidence";
dispute.ruled = false;
Expand All @@ -17,6 +18,7 @@ export function createDisputeFromEvent(event: DisputeCreation): void {
dispute.overridden = false;
dispute.lastPeriodChange = event.block.timestamp;
dispute.lastPeriodChangeBlockNumber = event.block.number;
dispute.periodNotificationIndex = getAndIncrementPeriodCounter(dispute.period);
const court = Court.load(courtID);
if (!court) return;
dispute.periodDeadline = event.block.timestamp.plus(court.timesPerPeriod[0]);
Expand Down
9 changes: 7 additions & 2 deletions subgraph/src/entities/Draw.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Draw as DrawEvent } from "../../generated/KlerosCore/KlerosCore";
import { Draw } from "../../generated/schema";
import { Draw, User } from "../../generated/schema";
import { getAndIncrementPeriodCounter } from "./PeriodIndexCounter";

export function createDrawFromEvent(event: DrawEvent): void {
const disputeID = event.params._disputeID.toString();
Expand All @@ -9,9 +10,13 @@ export function createDrawFromEvent(event: DrawEvent): void {
const drawID = `${disputeID}-${roundIndex.toString()}-${voteID.toString()}`;
const draw = new Draw(drawID);
draw.blockNumber = event.block.number;
const user = User.load(event.params._address.toHexString());
if (user && !user.disputes.includes(disputeID)) {
draw.drawNotificationIndex = getAndIncrementPeriodCounter("draw");
}
draw.dispute = disputeID;
draw.round = roundID;
draw.juror = event.params._address.toHexString();
draw.voteID = voteID;
draw.voteIDNum = voteID;
draw.save();
}
14 changes: 14 additions & 0 deletions subgraph/src/entities/PeriodIndexCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { PeriodIndexCounter } from "../../generated/schema";
import { BigInt } from "@graphprotocol/graph-ts";

export function getAndIncrementPeriodCounter(id: string): BigInt {
let counter = PeriodIndexCounter.load(id);
if (!counter) {
counter = new PeriodIndexCounter(id);
counter.counter = BigInt.fromI32(0);
}
const counterOld = counter.counter;
counter.counter = counter.counter.plus(BigInt.fromI32(1));
counter.save();
return counterOld;
}
1 change: 1 addition & 0 deletions subgraph/src/entities/Round.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function createRoundFromRoundInfo(
): void {
const roundID = `${disputeID.toString()}-${roundIndex.toString()}`;
const round = new Round(roundID);
round.isCurrentRound = true;
const feeToken = roundInfo.feeToken.toHexString();
round.feeToken = feeToken === "0x0000000000000000000000000000000000000000" ? null : feeToken;
round.disputeKit = roundInfo.disputeKitID.toString();
Expand Down
1 change: 1 addition & 0 deletions subgraph/src/entities/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function createUserFromAddress(id: string): User {
user.totalDelayed = ZERO;
user.activeDisputes = ZERO;
user.disputes = [];
user.rounds = [];
user.resolvedDisputes = [];
user.totalResolvedDisputes = ZERO;
user.totalAppealingDisputes = ZERO;
Expand Down
2 changes: 2 additions & 0 deletions subgraph/subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ dataSources:
handler: handleRuling
- event: AcceptedFeeToken(indexed address,indexed bool)
handler: handleAcceptedFeeToken
- event: CourtJump(indexed uint256,indexed uint256,indexed uint96,uint96)
handler: handleCourtJump
file: ./src/KlerosCore.ts
- kind: ethereum
name: PolicyRegistry
Expand Down
3 changes: 2 additions & 1 deletion web/src/components/DisputeCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useVotingHistory } from "queries/useVotingHistory";
import DisputeInfo from "./DisputeInfo";
import PeriodBanner from "./PeriodBanner";
import { isUndefined } from "utils/index";
import { getLocalRounds } from "utils/getLocalRounds";

const StyledCard = styled(Card)`
width: 100%;
Expand Down Expand Up @@ -104,7 +105,7 @@ const DisputeCard: React.FC<IDisputeCard> = ({ id, arbitrated, period, lastPerio
const courtName = courtPolicy?.name;
const category = disputeTemplate ? disputeTemplate.category : undefined;
const { data: votingHistory } = useVotingHistory(id);
const localRounds = votingHistory?.dispute?.disputeKitDispute?.localRounds;
const localRounds = getLocalRounds(votingHistory?.dispute?.disputeKitDispute);
const navigate = useNavigate();
return (
<>
Expand Down
11 changes: 6 additions & 5 deletions web/src/components/Verdict/DisputeTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import React, { useMemo } from "react";
import { useParams } from "react-router-dom";
import styled, { useTheme } from "styled-components";
import { _TimelineItem1, CustomTimeline } from "@kleros/ui-components-library";
import CalendarIcon from "assets/svgs/icons/calendar.svg";
import ClosedCaseIcon from "assets/svgs/icons/check-circle-outline.svg";
import AppealedCaseIcon from "assets/svgs/icons/close-circle.svg";
import { Periods } from "consts/periods";
import { ClassicRound } from "src/graphql/graphql";
import { getVoteChoice } from "pages/Cases/CaseDetails/Voting/VotingHistory";
import { DisputeDetailsQuery, useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
import { useDisputeTemplate } from "queries/useDisputeTemplate";
import { useVotingHistory } from "queries/useVotingHistory";
import CalendarIcon from "assets/svgs/icons/calendar.svg";
import ClosedCaseIcon from "assets/svgs/icons/check-circle-outline.svg";
import AppealedCaseIcon from "assets/svgs/icons/close-circle.svg";
import { getVoteChoice } from "pages/Cases/CaseDetails/Voting/VotingHistory";
import { getLocalRounds } from "utils/getLocalRounds";

const Container = styled.div`
display: flex;
Expand Down Expand Up @@ -63,7 +64,7 @@ const useItems = (disputeDetails?: DisputeDetailsQuery, arbitrable?: `0x${string
const { id } = useParams();
const { data: votingHistory } = useVotingHistory(id);
const { data: disputeTemplate } = useDisputeTemplate(id, arbitrable);
const localRounds: ClassicRound[] = votingHistory?.dispute?.disputeKitDispute?.localRounds as ClassicRound[];
const localRounds: ClassicRound[] = getLocalRounds(votingHistory?.dispute?.disputeKitDispute) as ClassicRound[];

const theme = useTheme();

Expand Down
2 changes: 1 addition & 1 deletion web/src/hooks/queries/useClassicAppealQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const classicAppealQuery = graphql(`
export const useClassicAppealQuery = (id?: string | number) => {
const isEnabled = id !== undefined;

return useQuery({
return useQuery<ClassicAppealQuery>({
queryKey: ["refetchOnBlock", `classicAppealQuery${id}`],
enabled: isEnabled,
queryFn: async () => await graphqlQueryFnHelper(classicAppealQuery, { disputeID: id?.toString() }),
Expand Down
4 changes: 2 additions & 2 deletions web/src/hooks/queries/useDrawQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export type { DrawQuery };
const drawQuery = graphql(`
query Draw($address: String, $disputeID: String, $roundID: String) {
draws(where: { dispute: $disputeID, juror: $address, round: $roundID }) {
voteID
voteIDNum
}
}
`);

export const useDrawQuery = (address?: string | null, disputeID?: string, roundID?: string) => {
const isEnabled = !!(address && disputeID && roundID);
return useQuery({
return useQuery<DrawQuery>({
queryKey: [`drawQuery${[address, disputeID, roundID]}`],
enabled: isEnabled,
queryFn: async () => await graphqlQueryFnHelper(drawQuery, { address, disputeID, roundID }),
Expand Down
3 changes: 2 additions & 1 deletion web/src/hooks/useClassicAppealContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useAppealCost } from "queries/useAppealCost";
import { useDisputeKitClassicMultipliers } from "queries/useDisputeKitClassicMultipliers";
import { useClassicAppealQuery, ClassicAppealQuery } from "queries/useClassicAppealQuery";
import { useCountdown } from "hooks/useCountdown";
import { getLocalRounds } from "utils/getLocalRounds";

const LoserSideCountdownContext = createContext<number | undefined>(undefined);

Expand Down Expand Up @@ -100,7 +101,7 @@ export const useOptionsContext = () => useContext(OptionsContext);
const getCurrentLocalRound = (dispute?: ClassicAppealQuery["dispute"]) => {
const period = dispute?.period;
const currentLocalRoundIndex = dispute?.disputeKitDispute?.currentLocalRoundIndex;
return dispute?.disputeKitDispute?.localRounds[
return getLocalRounds(dispute?.disputeKitDispute)[
["appeal", "execution"].includes(period ?? "") ? currentLocalRoundIndex : currentLocalRoundIndex - 1
];
};
Expand Down
3 changes: 2 additions & 1 deletion web/src/pages/Cases/CaseDetails/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { StyledSkeleton } from "components/StyledSkeleton";
import DisputeInfo from "components/DisputeCard/DisputeInfo";
import Verdict from "components/Verdict/index";
import { useVotingHistory } from "hooks/queries/useVotingHistory";
import { getLocalRounds } from "utils/getLocalRounds";

const Container = styled.div`
width: 100%;
Expand Down Expand Up @@ -117,7 +118,7 @@ const Overview: React.FC<IOverview> = ({ arbitrable, courtID, currentPeriodIndex
const { data: disputeDetails } = useDisputeDetailsQuery(id);
const { data: courtPolicy } = useCourtPolicy(courtID);
const { data: votingHistory } = useVotingHistory(id);
const localRounds = votingHistory?.dispute?.disputeKitDispute?.localRounds;
const localRounds = getLocalRounds(votingHistory?.dispute?.disputeKitDispute);
const courtName = courtPolicy?.name;
const court = disputeDetails?.dispute?.court;
const rewards = court ? `≥ ${formatEther(court.feeForJuror)} ETH` : undefined;
Expand Down
7 changes: 4 additions & 3 deletions web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useVotingHistory } from "queries/useVotingHistory";
import { useDisputeTemplate } from "queries/useDisputeTemplate";
import { shortenAddress } from "utils/shortenAddress";
import { isUndefined } from "utils/index";
import { getLocalRounds } from "utils/getLocalRounds";

const Container = styled.div``;

Expand Down Expand Up @@ -87,7 +88,7 @@ const AccordionContent: React.FC<{
);
};

export const getVoteChoice = (vote, answers) => {
export const getVoteChoice = (vote: number, answers: { title: string }[]) => {
const selectedAnswer = answers?.[vote - 1]?.title;
if (vote === 0) {
return "Refuse to arbitrate";
Expand All @@ -104,7 +105,7 @@ const VotingHistory: React.FC<{ arbitrable?: `0x${string}`; isQuestion: boolean
const [currentTab, setCurrentTab] = useState(0);
const { data: disputeTemplate } = useDisputeTemplate(id, arbitrable);
const rounds = votingHistory?.dispute?.rounds;
const localRounds = votingHistory?.dispute?.disputeKitDispute?.localRounds;
const localRounds = getLocalRounds(votingHistory?.dispute?.disputeKitDispute);
const answers = disputeTemplate?.answers;

return (
Expand All @@ -115,7 +116,7 @@ const VotingHistory: React.FC<{ arbitrable?: `0x${string}`; isQuestion: boolean
{isQuestion && disputeTemplate.question ? (
<ReactMarkdown>{disputeTemplate.question}</ReactMarkdown>
) : (
<ReactMarkdown>The dispute's template is not correct please vote refuse to arbitrate</ReactMarkdown>
<ReactMarkdown>{"The dispute's template is not correct please vote refuse to arbitrate"}</ReactMarkdown>
)}
<StyledTabs
currentValue={currentTab}
Expand Down
8 changes: 6 additions & 2 deletions web/src/pages/Cases/CaseDetails/Voting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const Voting: React.FC<IVoting> = ({ arbitrable, currentPeriodIndex }) => {
const { data: appealCost } = useAppealCost(id);
const { data: drawData } = useDrawQuery(address?.toLowerCase(), id, disputeData?.dispute?.currentRound.id);
const roundId = disputeData?.dispute?.currentRoundIndex;
const voteId = drawData?.draws?.[0]?.voteID;
const voteId = drawData?.draws?.[0]?.voteIDNum;
const { data: voted } = useDisputeKitClassicIsVoteActive({
enabled: !isUndefined(roundId) && !isUndefined(voteId),
args: [BigInt(id ?? 0), roundId, voteId],
Expand Down Expand Up @@ -101,7 +101,11 @@ const Voting: React.FC<IVoting> = ({ arbitrable, currentPeriodIndex }) => {
!voted ? (
<>
<VotingHistory {...{ arbitrable }} isQuestion={false} />
<Classic {...{ arbitrable }} setIsOpen={setIsPopupOpen} voteIDs={drawData.draws.map((draw) => draw.voteID)} />
<Classic
{...{ arbitrable }}
setIsOpen={setIsPopupOpen}
voteIDs={drawData.draws.map((draw) => draw.voteIDNum)}
/>
</>
) : (
<VotingHistory {...{ arbitrable }} isQuestion={true} />
Expand Down
20 changes: 20 additions & 0 deletions web/src/utils/getLocalRounds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { VotingHistoryQuery } from "queries/useVotingHistory";
import { ClassicAppealQuery } from "queries/useClassicAppealQuery";

type IVotingHistoryLocalRounds = NonNullable<
NonNullable<VotingHistoryQuery["dispute"]>["disputeKitDispute"]
>["localRounds"];

type IClassicAppealQueryLocalRounds = NonNullable<
NonNullable<ClassicAppealQuery["dispute"]>["disputeKitDispute"]
>["localRounds"];

type ILocalRounds = IClassicAppealQueryLocalRounds | IVotingHistoryLocalRounds;

interface IDisputeKitDisputes {
localRounds: ILocalRounds;
}

export const getLocalRounds = (disputeKitDisputes: IDisputeKitDisputes | undefined | null) => {
return disputeKitDisputes?.reduce<ILocalRounds>((acc: ILocalRounds, { localRounds }) => acc.concat(localRounds), []);
};