Skip to content

Commit 5ac64f5

Browse files
committed
feat: shutter support in dispute commiting
1 parent 0dad7c4 commit 5ac64f5

File tree

13 files changed

+357
-4
lines changed

13 files changed

+357
-4
lines changed

web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"@reown/appkit-adapter-wagmi": "^1.7.1",
9595
"@sentry/react": "^7.120.0",
9696
"@sentry/tracing": "^7.120.0",
97+
"@shutter-network/shutter-sdk": "^0.0.1",
9798
"@solana/wallet-adapter-react": "^0.15.36",
9899
"@solana/web3.js": "^1.98.0",
99100
"@tanstack/react-query": "^5.69.0",

web/src/hooks/queries/useDisputeDetailsQuery.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const disputeDetailsQuery = graphql(`
2828
currentRound {
2929
id
3030
nbVotes
31+
disputeKit {
32+
id
33+
}
3134
}
3235
currentRoundIndex
3336
isCrossChain

web/src/pages/Cases/CaseDetails/Voting/Classic/Commit.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { wrapWithToast } from "utils/wrapWithToast";
1313

1414
import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
1515

16-
import OptionsContainer from "./OptionsContainer";
16+
import OptionsContainer from "../OptionsContainer";
1717

1818
const Container = styled.div`
1919
width: 100%;

web/src/pages/Cases/CaseDetails/Voting/Classic/Reveal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
1919

2020
import InfoCard from "components/InfoCard";
2121

22-
import JustificationArea from "./JustificationArea";
22+
import JustificationArea from "../JustificationArea";
2323
import { Answer } from "@kleros/kleros-sdk";
2424
import { EnsureChain } from "components/EnsureChain";
2525

web/src/pages/Cases/CaseDetails/Voting/Classic/Vote.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { wrapWithToast } from "utils/wrapWithToast";
99

1010
import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
1111

12-
import OptionsContainer from "./OptionsContainer";
12+
import OptionsContainer from "../OptionsContainer";
1313

1414
const Container = styled.div`
1515
width: 100%;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import React, { useCallback, useMemo, useState } from "react";
2+
import styled from "styled-components";
3+
import { useParams } from "react-router-dom";
4+
import { useLocalStorage } from "react-use";
5+
import { keccak256, encodePacked } from "viem";
6+
import { useWalletClient, usePublicClient, useConfig } from "wagmi";
7+
8+
import { simulateDisputeKitShutterCastCommit } from "hooks/contracts/generated";
9+
import useSigningAccount from "hooks/useSigningAccount";
10+
import { isUndefined } from "utils/index";
11+
import { wrapWithToast } from "utils/wrapWithToast";
12+
import { encrypt } from "utils/shutter";
13+
import OptionsContainer from "../OptionsContainer";
14+
15+
const Container = styled.div`
16+
width: 100%;
17+
height: auto;
18+
`;
19+
20+
interface ICommit {
21+
arbitrable: `0x${string}`;
22+
voteIDs: string[];
23+
setIsOpen: (val: boolean) => void;
24+
refetch: () => void;
25+
}
26+
27+
const SEPARATOR = "␟";
28+
29+
const Commit: React.FC<ICommit> = ({ arbitrable, voteIDs, setIsOpen, refetch }) => {
30+
const { id } = useParams();
31+
const parsedDisputeID = useMemo(() => BigInt(id ?? 0), [id]);
32+
const parsedVoteIDs = useMemo(() => voteIDs.map((voteID) => BigInt(voteID)), [voteIDs]);
33+
const { data: walletClient } = useWalletClient();
34+
const publicClient = usePublicClient();
35+
const wagmiConfig = useConfig();
36+
const { signingAccount, generateSigningAccount } = useSigningAccount();
37+
const [justification, setJustification] = useState("");
38+
const saltKey = useMemo(() => `shutter-dispute-${id}-voteids-${voteIDs}`, [id, voteIDs]);
39+
const [_, setSalt] = useLocalStorage(saltKey);
40+
41+
const handleCommit = useCallback(
42+
async (choice: bigint) => {
43+
const message = { message: saltKey };
44+
const rawSalt = !isUndefined(signingAccount)
45+
? await signingAccount.signMessage(message)
46+
: await (async () => {
47+
const account = await generateSigningAccount();
48+
return await account?.signMessage(message);
49+
})();
50+
if (isUndefined(rawSalt)) return;
51+
52+
const salt = keccak256(rawSalt);
53+
setSalt(JSON.stringify({ salt, choice: choice.toString(), justification }));
54+
55+
const encodedMessage = `${choice.toString()}${SEPARATOR}${salt}${SEPARATOR}${justification}`;
56+
const { encryptedCommitment, identity } = await encrypt(encodedMessage);
57+
58+
const commitHash = keccak256(
59+
encodePacked(["uint256", "uint256", "string"], [choice, BigInt(salt), justification])
60+
);
61+
62+
const { request } = await simulateDisputeKitShutterCastCommit(wagmiConfig, {
63+
args: [parsedDisputeID, parsedVoteIDs, commitHash, identity as `0x${string}`, encryptedCommitment],
64+
});
65+
if (walletClient && publicClient) {
66+
await wrapWithToast(async () => await walletClient.writeContract(request), publicClient).then(({ status }) => {
67+
setIsOpen(status);
68+
});
69+
}
70+
refetch();
71+
},
72+
[
73+
wagmiConfig,
74+
justification,
75+
saltKey,
76+
setSalt,
77+
parsedVoteIDs,
78+
parsedDisputeID,
79+
publicClient,
80+
setIsOpen,
81+
walletClient,
82+
generateSigningAccount,
83+
signingAccount,
84+
refetch,
85+
]
86+
);
87+
88+
return id ? (
89+
<Container>
90+
<OptionsContainer
91+
{...{
92+
arbitrable,
93+
justification,
94+
setJustification,
95+
handleSelection: handleCommit,
96+
}}
97+
/>
98+
</Container>
99+
) : null;
100+
};
101+
102+
export default Commit;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import React from "react";
2+
3+
const Vote: React.FC = () => null;
4+
5+
export default Vote;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React, { useMemo } from "react";
2+
import { useParams } from "react-router-dom";
3+
import { useAccount } from "wagmi";
4+
5+
import { useDrawQuery } from "hooks/queries/useDrawQuery";
6+
import { useVotingContext } from "hooks/useVotingContext";
7+
import { useDisputeDetailsQuery } from "queries/useDisputeDetailsQuery";
8+
9+
import ShutterCommit from "./Commit";
10+
11+
interface IShutter {
12+
arbitrable: `0x${string}`;
13+
setIsOpen: (val: boolean) => void;
14+
}
15+
16+
const Shutter: React.FC<IShutter> = ({ arbitrable, setIsOpen }) => {
17+
const { id } = useParams();
18+
const { address } = useAccount();
19+
const { data: disputeData } = useDisputeDetailsQuery(id);
20+
const { data: drawData, refetch } = useDrawQuery(address?.toLowerCase(), id, disputeData?.dispute?.currentRound.id);
21+
const { isCommitPeriod, commited } = useVotingContext();
22+
const voteIDs = useMemo(() => drawData?.draws?.map((draw) => draw.voteIDNum) as string[], [drawData]);
23+
24+
return id && isCommitPeriod && !commited ? <ShutterCommit {...{ arbitrable, setIsOpen, voteIDs, refetch }} /> : null;
25+
};
26+
27+
export default Shutter;

web/src/pages/Cases/CaseDetails/Voting/index.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ import InfoCard from "components/InfoCard";
2525
import Popup, { PopupType } from "components/Popup";
2626

2727
import Classic from "./Classic";
28+
import Shutter from "./Shutter";
2829
import VotingHistory from "./VotingHistory";
2930

31+
import { getDisputeKitName } from "consts/index";
32+
3033
const Container = styled.div`
3134
padding: 20px 16px 16px;
3235
@@ -66,6 +69,11 @@ const Voting: React.FC<IVoting> = ({ arbitrable, currentPeriodIndex }) => {
6669
const timesPerPeriod = disputeData?.dispute?.court?.timesPerPeriod;
6770
const finalDate = useFinalDate(lastPeriodChange, currentPeriodIndex, timesPerPeriod);
6871

72+
const disputeKitId = disputeData?.dispute?.currentRound?.disputeKit?.id;
73+
const disputeKitName = disputeKitId ? getDisputeKitName(Number(disputeKitId)) : undefined;
74+
const isClassicDisputeKit = disputeKitName?.toLowerCase().includes("classic") ?? false;
75+
const isShutterDisputeKit = disputeKitName?.toLowerCase().includes("shutter") ?? false;
76+
6977
const isCommitOrVotePeriod = useMemo(
7078
() => [Periods.vote, Periods.commit].includes(currentPeriodIndex),
7179
[currentPeriodIndex]
@@ -107,7 +115,8 @@ const Voting: React.FC<IVoting> = ({ arbitrable, currentPeriodIndex }) => {
107115
{userWasDrawn && isCommitOrVotePeriod && !voted ? (
108116
<>
109117
<VotingHistory {...{ arbitrable }} isQuestion={false} />
110-
<Classic arbitrable={arbitrable ?? "0x0"} setIsOpen={setIsPopupOpen} />
118+
{isClassicDisputeKit ? <Classic arbitrable={arbitrable ?? "0x0"} setIsOpen={setIsPopupOpen} /> : null}
119+
{isShutterDisputeKit ? <Shutter arbitrable={arbitrable ?? "0x0"} setIsOpen={setIsPopupOpen} /> : null}
111120
</>
112121
) : (
113122
<VotingHistory {...{ arbitrable }} isQuestion={true} />

0 commit comments

Comments
 (0)