Skip to content

Commit

Permalink
Merge branch 'dev' into feat(web)/x-share-juror-score
Browse files Browse the repository at this point in the history
  • Loading branch information
kemuru authored Oct 26, 2023
2 parents 025f02f + 7f8be8e commit 78b0fd6
Show file tree
Hide file tree
Showing 26 changed files with 760 additions and 233 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy-subgraph.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
default: 'arbitrum-goerli'
type: choice
options:
- arbitrum-goerli-devnet
- arbitrum-goerli
- arbitrum
update:
Expand Down
4 changes: 3 additions & 1 deletion subgraph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"update:local": "./scripts/update.sh localhost mainnet",
"codegen": "graph codegen",
"build": "graph build",
"test": "graph test",
"clean": "graph clean && rm subgraph.yaml.bak.*",
"deploy:arbitrum-goerli": "graph deploy --product hosted-service kleros/kleros-v2-core-testnet-2",
"deploy:arbitrum-goerli-devnet": "graph deploy --product hosted-service kleros/kleros-v2-core-devnet",
Expand All @@ -30,7 +31,8 @@
"@graphprotocol/graph-cli": "0.52.0",
"@kleros/kleros-v2-eslint-config": "workspace:^",
"@kleros/kleros-v2-prettier-config": "workspace:^",
"gluegun": "^5.1.2"
"gluegun": "^5.1.2",
"matchstick-as": "0.6.0-beta.2"
},
"dependenciesComments": {
"@graphprotocol/graph-cli": "pinned because of this issue: https://github.com/graphprotocol/graph-tooling/issues/1399#issuecomment-1676104540"
Expand Down
3 changes: 3 additions & 0 deletions subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface DisputeKitDispute {
coreDispute: Dispute!
localRounds: [DisputeKitRound!]! @derivedFrom(field: "localDispute")
currentLocalRoundIndex: BigInt!
timestamp: BigInt!
}

interface DisputeKitRound {
Expand Down Expand Up @@ -72,6 +73,7 @@ type User @entity {
totalResolvedDisputes: BigInt!
totalDisputes: BigInt!
totalCoherent: BigInt!
coherenceScore: BigInt!
totalAppealingDisputes: BigInt!
votes: [Vote!]! @derivedFrom(field: "juror")
contributions: [Contribution!]! @derivedFrom(field: "contributor")
Expand Down Expand Up @@ -236,6 +238,7 @@ type ClassicDispute implements DisputeKitDispute @entity {
coreDispute: Dispute!
localRounds: [DisputeKitRound!]! @derivedFrom(field: "localDispute")
currentLocalRoundIndex: BigInt!
timestamp: BigInt!

numberOfChoices: BigInt!
extraData: Bytes!
Expand Down
1 change: 1 addition & 0 deletions subgraph/src/entities/ClassicDispute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export function createClassicDisputeFromEvent(event: DisputeCreation): void {
classicDispute.currentLocalRoundIndex = ZERO;
classicDispute.numberOfChoices = event.params._numberOfChoices;
classicDispute.extraData = event.params._extraData;
classicDispute.timestamp = event.block.timestamp;
classicDispute.save();
}
18 changes: 17 additions & 1 deletion subgraph/src/entities/User.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import { BigInt } from "@graphprotocol/graph-ts";
import { BigInt, BigDecimal } from "@graphprotocol/graph-ts";
import { User } from "../../generated/schema";
import { ONE, ZERO } from "../utils";

export function computeCoherenceScore(totalCoherent: BigInt, totalResolvedDisputes: BigInt): BigInt {
const smoothingFactor = BigDecimal.fromString("10");

let denominator = totalResolvedDisputes.toBigDecimal().plus(smoothingFactor);
let coherencyRatio = totalCoherent.toBigDecimal().div(denominator);

const coherencyScore = coherencyRatio.times(BigDecimal.fromString("100"));

const roundedScore = coherencyScore.plus(BigDecimal.fromString("0.5"));

return BigInt.fromString(roundedScore.toString().split(".")[0]);
}

export function ensureUser(id: string): User {
const user = User.load(id);

Expand All @@ -24,6 +37,7 @@ export function createUserFromAddress(id: string): User {
user.totalAppealingDisputes = ZERO;
user.totalDisputes = ZERO;
user.totalCoherent = ZERO;
user.coherenceScore = ZERO;
user.save();

return user;
Expand Down Expand Up @@ -52,6 +66,7 @@ export function resolveUserDispute(id: string, previousFeeAmount: BigInt, feeAmo
user.totalCoherent = user.totalCoherent.plus(ONE);
}
}
user.coherenceScore = computeCoherenceScore(user.totalCoherent, user.totalResolvedDisputes);
user.save();
return;
}
Expand All @@ -61,5 +76,6 @@ export function resolveUserDispute(id: string, previousFeeAmount: BigInt, feeAmo
user.totalCoherent = user.totalCoherent.plus(ONE);
}
user.activeDisputes = user.activeDisputes.minus(ONE);
user.coherenceScore = computeCoherenceScore(user.totalCoherent, user.totalResolvedDisputes);
user.save();
}
9 changes: 9 additions & 0 deletions subgraph/tests/user.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { assert, test, describe } from "matchstick-as/assembly/index";
import { BigInt } from "@graphprotocol/graph-ts";
import { computeCoherenceScore } from "../src/entities/User";

describe("Compute coherence score", () => {
test("Slam BigInts together", () => {
assert.bigIntEquals(BigInt.fromI32(8), computeCoherenceScore(BigInt.fromI32(1), BigInt.fromI32(2)));
});
});
23 changes: 19 additions & 4 deletions web/src/components/ConnectWallet/AccountDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,15 @@ const StyledAvatar = styled.img<{ size: `${number}` }>`
height: ${({ size }) => size + "px"};
`;

export const IdenticonOrAvatar: React.FC<{ size: `${number}` }> = ({ size } = { size: "16" }) => {
const { address } = useAccount();
interface IIdenticonOrAvatar {
size?: `${number}`;
address?: `0x${string}`;
}

export const IdenticonOrAvatar: React.FC<IIdenticonOrAvatar> = ({ size = "16", address: propAddress }) => {
const { address: defaultAddress } = useAccount();
const address = propAddress || defaultAddress;

const { data: name } = useEnsName({
address,
chainId: 1,
Expand All @@ -106,19 +113,27 @@ export const IdenticonOrAvatar: React.FC<{ size: `${number}` }> = ({ size } = {
name,
chainId: 1,
});

return avatar ? (
<StyledAvatar src={avatar} alt="avatar" size={size} />
) : (
<StyledIdenticon size={size} string={address} />
);
};

export const AddressOrName: React.FC = () => {
const { address } = useAccount();
interface IAddressOrName {
address?: `0x${string}`;
}

export const AddressOrName: React.FC<IAddressOrName> = ({ address: propAddress }) => {
const { address: defaultAddress } = useAccount();
const address = propAddress || defaultAddress;

const { data } = useEnsName({
address,
chainId: 1,
});

return <label>{data ?? (address && shortenAddress(address))}</label>;
};

Expand Down
6 changes: 3 additions & 3 deletions web/src/components/Popup/Description/StakeWithdraw.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import styled from "styled-components";
import { isUndefined } from "utils/index";
import { formatUnits } from "viem";
import { useAccount } from "wagmi";
import { isUndefined } from "utils/index";
import { useKlerosCoreGetJurorBalance } from "hooks/contracts/generated";
import { format } from "src/pages/Dashboard/Courts/CourtCard";
import KlerosLogo from "tsx:svgs/icons/kleros.svg";

const Container = styled.div`
Expand Down Expand Up @@ -88,7 +88,7 @@ const StakeWithdraw: React.FC<IStakeWithdraw> = ({ pnkStaked, courtName, isStake

<TotalStakeContainer>
<StyledKlerosLogo /> <MyStakeContainer>My Stake:</MyStakeContainer>{" "}
<AmountContainer>{`${format(jurorBalance?.[0])} PNK`} </AmountContainer>
<AmountContainer>{`${formatUnits(jurorBalance?.[0] ?? BigInt(0), 18)} PNK`} </AmountContainer>
</TotalStakeContainer>
</Container>
);
Expand Down
13 changes: 10 additions & 3 deletions web/src/hooks/queries/useClassicAppealQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { graphqlQueryFnHelper } from "utils/graphqlQueryFnHelper";
export type { ClassicAppealQuery };

const classicAppealQuery = graphql(`
query ClassicAppeal($disputeID: ID!) {
query ClassicAppeal($disputeID: ID!, $orderBy: DisputeKitDispute_orderBy, $orderDirection: OrderDirection) {
dispute(id: $disputeID) {
period
court {
Expand All @@ -16,7 +16,7 @@ const classicAppealQuery = graphql(`
id
}
lastPeriodChange
disputeKitDispute {
disputeKitDispute(orderBy: $orderBy, orderDirection: $orderDirection) {
id
currentLocalRoundIndex
localRounds {
Expand All @@ -37,6 +37,13 @@ export const useClassicAppealQuery = (id?: string | number) => {
return useQuery<ClassicAppealQuery>({
queryKey: ["refetchOnBlock", `classicAppealQuery${id}`],
enabled: isEnabled,
queryFn: async () => await graphqlQueryFnHelper(classicAppealQuery, { disputeID: id?.toString() }),
queryFn: async () =>
isEnabled
? await graphqlQueryFnHelper(classicAppealQuery, {
disputeID: id?.toString(),
orderBy: "timestamp",
orderDirection: "asc",
})
: undefined,
});
};
35 changes: 35 additions & 0 deletions web/src/hooks/queries/useTopUsersByCoherenceScore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useQuery } from "@tanstack/react-query";
import { graphql } from "src/graphql";
import { graphqlQueryFnHelper } from "utils/graphqlQueryFnHelper";
import { TopUsersByCoherenceScoreQuery } from "src/graphql/graphql";
import { isUndefined } from "utils/index";
export type { TopUsersByCoherenceScoreQuery };

const topUsersByCoherenceScoreQuery = graphql(`
query TopUsersByCoherenceScore($first: Int!, $orderBy: User_orderBy, $orderDirection: OrderDirection) {
users(first: $first, orderBy: $orderBy, orderDirection: $orderDirection) {
id
coherenceScore
totalCoherent
totalResolvedDisputes
}
}
`);

export const useTopUsersByCoherenceScore = (first = 5) => {
const isEnabled = !isUndefined(first);

return useQuery<TopUsersByCoherenceScoreQuery>({
queryKey: [`TopUsersByCoherenceScore${first}`],
enabled: isEnabled,
staleTime: Infinity,
queryFn: async () =>
isEnabled
? await graphqlQueryFnHelper(topUsersByCoherenceScoreQuery, {
first: first,
orderBy: "coherenceScore",
orderDirection: "desc",
})
: undefined,
});
};
1 change: 1 addition & 0 deletions web/src/hooks/queries/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const userFragment = graphql(`
totalResolvedDisputes
totalAppealingDisputes
totalCoherent
coherenceScore
tokens {
court {
id
Expand Down
2 changes: 1 addition & 1 deletion web/src/hooks/useClassicAppealContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const getCurrentLocalRound = (dispute?: ClassicAppealQuery["dispute"]) => {
if (!dispute) return undefined;

const period = dispute.period;
const currentLocalRoundIndex = dispute.disputeKitDispute?.currentLocalRoundIndex;
const currentLocalRoundIndex = dispute.disputeKitDispute.at(-1)?.currentLocalRoundIndex;
const adjustedRoundIndex = ["appeal", "execution"].includes(period)
? currentLocalRoundIndex
: currentLocalRoundIndex - 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useOptionsContext,
useSelectedOptionContext,
} from "hooks/useClassicAppealContext";
import { isUndefined } from "utils/index";
import { formatUnitsWei } from "utils/format";

const Container = styled.div`
Expand All @@ -30,14 +31,15 @@ const StageOne: React.FC<IStageOne> = ({ setAmount }) => {
const options = useOptionsContext();
const loserSideCountdown = useLoserSideCountdownContext();
const { selectedOption, setSelectedOption } = useSelectedOptionContext();

return (
<Container>
<StageExplainer {...{ loserSideCountdown }} />
<label> Which option do you want to fund? </label>
<OptionsContainer>
{typeof paidFees !== "undefined" &&
typeof winnerRequiredFunding !== "undefined" &&
typeof loserRequiredFunding !== "undefined" &&
{!isUndefined(paidFees) &&
!isUndefined(winnerRequiredFunding) &&
!isUndefined(loserRequiredFunding) &&
options?.map((answer: string, i: number) => {
const requiredFunding = i.toString() === winningChoice ? winnerRequiredFunding : loserRequiredFunding;
return (
Expand Down
45 changes: 21 additions & 24 deletions web/src/pages/Dashboard/Courts/CourtCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import styled from "styled-components";
import { useAccount } from "wagmi";
import { formatEther } from "viem";
import { formatUnits } from "viem";
import { Card as _Card, Breadcrumb } from "@kleros/ui-components-library";
import WithHelpTooltip from "../WithHelpTooltip";
import { isUndefined } from "utils/index";
Expand Down Expand Up @@ -33,8 +33,6 @@ const tooltipMsg =
"The locked stake of incoherent jurors is redistributed as incentives for " +
"the coherent jurors.";

export const format = (value: bigint | undefined): string => (value !== undefined ? formatEther(value) : "0");

interface ICourtCard {
id: string;
name: string;
Expand All @@ -44,31 +42,30 @@ const CourtCard: React.FC<ICourtCard> = ({ id, name }) => {
const { address } = useAccount();
const { data: jurorBalance } = useKlerosCoreGetJurorBalance({
enabled: !isUndefined(address),
args: [address, id],
args: [address!, BigInt(id)],
watch: true,
});

const stake = format(jurorBalance?.[0]);
const lockedStake = format(jurorBalance?.[1]);
const stake = jurorBalance?.[0] ?? BigInt(0);
const lockedStake = jurorBalance?.[1] ?? BigInt(0);
const formatedStake = formatUnits(stake, 18);
const formatedLockedStake = formatUnits(lockedStake, 18);

return (
stake !== "0" ||
(lockedStake !== "0" && (
<Card>
<StyledBreadcrumb items={[{ text: name, value: 0 }]} />
<ValueContainer>
<label> Stake: </label>
<small>{`${stake} PNK`}</small>
</ValueContainer>
<ValueContainer>
<WithHelpTooltip {...{ place: "bottom", tooltipMsg }}>
<label> Locked Stake: </label>
</WithHelpTooltip>
<small>{`${lockedStake} PNK`}</small>
</ValueContainer>
</Card>
))
);
return stake > 0 || lockedStake > 0 ? (
<Card>
<StyledBreadcrumb items={[{ text: name, value: 0 }]} />
<ValueContainer>
<label> Stake: </label>
<small>{`${formatedStake} PNK`}</small>
</ValueContainer>
<ValueContainer>
<WithHelpTooltip {...{ place: "bottom", tooltipMsg }}>
<label> Locked Stake: </label>
</WithHelpTooltip>
<small>{`${formatedLockedStake} PNK`}</small>
</ValueContainer>
</Card>
) : null;
};

export default CourtCard;
Loading

0 comments on commit 78b0fd6

Please sign in to comment.