Skip to content

Commit bf8e8b8

Browse files
feat(web-devtools): ruler-functionality
1 parent 71e7d66 commit bf8e8b8

13 files changed

+556
-68
lines changed

web-devtools/.env.local.example

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
export NEXT_PUBLIC_ALCHEMY_API_KEY=
33
export NEXT_PUBLIC_DEPLOYMENT=devnet
44
export NEXT_PUBLIC_CORE_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-core-devnet/version/latest
5-
export NEXT_PUBLIC_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest
5+
export NEXT_PUBLIC_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest
6+
export NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=

web-devtools/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"react": "^18.2.0",
4444
"react-dom": "^18.2.0",
4545
"react-markdown": "^8.0.7",
46+
"react-toastify": "^10.0.5",
4647
"typewriter-effect": "^2.21.0",
4748
"vanilla-jsoneditor": "^0.21.4",
4849
"viem": "^2.1.0",

web-devtools/src/app/(main)/layout.tsx

+18-5
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,35 @@
22
import React from "react";
33
import styled from "styled-components";
44

5+
import "react-toastify/dist/ReactToastify.css";
56
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
7+
import { ToastContainer } from "react-toastify";
68

79
import GraphqlBatcherProvider from "context/GraphqlBatcher";
10+
import Web3Provider from "context/Web3Provider";
811

912
const Main = styled.main`
1013
min-height: calc(100vh - 130px);
1114
`;
1215
const queryClient = new QueryClient();
1316

17+
const StyledToastContainer = styled(ToastContainer)`
18+
padding: 16px;
19+
padding-top: 70px;
20+
`;
21+
1422
const Layout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
1523
return (
16-
<QueryClientProvider client={queryClient}>
17-
<GraphqlBatcherProvider>
18-
<Main>{children}</Main>
19-
</GraphqlBatcherProvider>
20-
</QueryClientProvider>
24+
<Web3Provider>
25+
<QueryClientProvider client={queryClient}>
26+
<GraphqlBatcherProvider>
27+
<Main>
28+
<StyledToastContainer />
29+
{children}
30+
</Main>
31+
</GraphqlBatcherProvider>
32+
</QueryClientProvider>
33+
</Web3Provider>
2134
);
2235
};
2336

web-devtools/src/app/(main)/ruler/ChangeDeveloper.tsx

+53-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
import React from "react";
1+
import React, { useCallback, useMemo, useState } from "react";
22
import styled from "styled-components";
33

4+
import { Address, isAddress } from "viem";
5+
import { usePublicClient } from "wagmi";
6+
47
import { Button } from "@kleros/ui-components-library";
58

9+
import { useRulerContext } from "context/RulerContext";
10+
import { useSimulateKlerosCoreRulerChangeRuler, useWriteKlerosCoreRulerChangeRuler } from "hooks/contracts/generated";
11+
import { isUndefined } from "utils/isUndefined";
12+
import { wrapWithToast } from "utils/wrapWithToast";
13+
614
import LabeledInput from "components/LabeledInput";
715

816
import Header from "./Header";
@@ -25,14 +33,55 @@ const StyledLabel = styled.label`
2533
`;
2634

2735
const ChangeDeveloper: React.FC = () => {
36+
const { arbitrable, currentDeveloper, refetchData, isRulerOfArbitrable } = useRulerContext();
37+
const [newDeveloper, setNewDeveloper] = useState<Address>("" as `0x${string}`);
38+
const [isChanging, setIsChanging] = useState(false);
39+
const publicClient = usePublicClient();
40+
41+
const isValid = useMemo(
42+
() => !isUndefined(newDeveloper) && (newDeveloper === ("" as `0x${string}`) || isAddress(newDeveloper)),
43+
[newDeveloper]
44+
);
45+
46+
const {
47+
data: changeRulerConfig,
48+
isLoading,
49+
isError,
50+
} = useSimulateKlerosCoreRulerChangeRuler({
51+
query: {
52+
enabled: !isUndefined(arbitrable) && !isUndefined(newDeveloper) && isRulerOfArbitrable,
53+
},
54+
args: [(arbitrable ?? "") as Address, (newDeveloper ?? "") as Address],
55+
});
56+
57+
const { writeContractAsync: changeRuler } = useWriteKlerosCoreRulerChangeRuler();
58+
59+
const handleClick = useCallback(() => {
60+
if (!publicClient || !changeRulerConfig) return;
61+
setIsChanging(true);
62+
wrapWithToast(async () => changeRuler(changeRulerConfig.request), publicClient)
63+
.then(() => refetchData())
64+
.finally(() => setIsChanging(false));
65+
}, [publicClient, changeRulerConfig, changeRuler, refetchData]);
66+
2867
return (
2968
<Container>
3069
<Header text="Developer" />
3170
<InputContainer>
32-
<StyledLabel>Current Developer : 0xbe8d95497E53aB41d5A45CC8def90d0e59b49f99</StyledLabel>
33-
<LabeledInput label="New Developer" />
71+
<StyledLabel>Current Developer : {currentDeveloper ?? "None"}</StyledLabel>
72+
<LabeledInput
73+
label="New Developer"
74+
onChange={(e) => setNewDeveloper(e.target.value as Address)}
75+
message={isValid ? "" : "Invalid Address"}
76+
variant={isValid ? "" : "error"}
77+
/>
3478
</InputContainer>
35-
<Button text="Update" />
79+
<Button
80+
text="Update"
81+
onClick={handleClick}
82+
isLoading={isLoading || isChanging}
83+
disabled={isError || isLoading || isChanging || isUndefined(arbitrable) || !isRulerOfArbitrable}
84+
/>
3685
</Container>
3786
);
3887
};

web-devtools/src/app/(main)/ruler/ManualRuling.tsx

+71-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1-
import React, { useState } from "react";
1+
import React, { useCallback, useMemo, useState } from "react";
22
import styled from "styled-components";
33

4+
import { RULING_MODE } from "consts";
5+
import { usePublicClient } from "wagmi";
6+
47
import { Button } from "@kleros/ui-components-library";
58

9+
import { useRulerContext } from "context/RulerContext";
10+
import {
11+
useSimulateKlerosCoreRulerChangeRulingModeToManual,
12+
useSimulateKlerosCoreRulerExecuteRuling,
13+
useWriteKlerosCoreRulerChangeRulingModeToManual,
14+
useWriteKlerosCoreRulerExecuteRuling,
15+
} from "hooks/contracts/generated";
16+
import { isUndefined } from "utils/isUndefined";
17+
import { wrapWithToast } from "utils/wrapWithToast";
18+
619
import LabeledInput from "components/LabeledInput";
720

821
import Header from "./Header";
@@ -22,10 +35,58 @@ const SelectContainer = styled.div`
2235
`;
2336

2437
const ManualRuling: React.FC = () => {
25-
const [tie, setTie] = useState<boolean>(false);
26-
const [overriden, setOverriden] = useState<boolean>(false);
38+
const [isSending, setIsSending] = useState<boolean>(false);
39+
const { arbitrable, arbitrableSettings } = useRulerContext();
40+
const [tie, setTie] = useState(arbitrableSettings?.tied ?? false);
41+
const [overriden, setOverriden] = useState(arbitrableSettings?.overidden ?? false);
42+
const [ruling, setRuling] = useState(arbitrableSettings?.ruling);
2743
const [disputeId, setDisputeId] = useState<number>();
28-
const [ruling, setRuling] = useState<number>();
44+
45+
const publicClient = usePublicClient();
46+
47+
const { data: manualModeConfig } = useSimulateKlerosCoreRulerChangeRulingModeToManual({
48+
query: {
49+
enabled: arbitrableSettings?.rulingMode !== RULING_MODE.Manual && !isUndefined(arbitrable),
50+
},
51+
args: [arbitrable as `0x${string}`],
52+
});
53+
const { writeContractAsync: changeToManualMode } = useWriteKlerosCoreRulerChangeRulingModeToManual();
54+
55+
const isDisabled = useMemo(() => {
56+
return isUndefined(disputeId) || isUndefined(ruling) || isUndefined(arbitrable);
57+
}, [disputeId, ruling, arbitrable]);
58+
59+
const {
60+
data: executeConfig,
61+
isLoading: isLoadingExecuteConfig,
62+
isError,
63+
} = useSimulateKlerosCoreRulerExecuteRuling({
64+
query: {
65+
enabled: arbitrableSettings?.rulingMode === RULING_MODE.Manual && !isUndefined(arbitrable) && !isDisabled,
66+
},
67+
args: [BigInt(disputeId ?? 0), BigInt(ruling ?? 0), tie, overriden],
68+
});
69+
70+
const { writeContractAsync: executeRuling } = useWriteKlerosCoreRulerExecuteRuling();
71+
72+
const handleRuling = useCallback(async () => {
73+
if (!publicClient) return;
74+
setIsSending(true);
75+
if (arbitrableSettings?.rulingMode !== RULING_MODE.Manual) {
76+
if (!manualModeConfig) return;
77+
wrapWithToast(async () => await changeToManualMode(manualModeConfig.request), publicClient)
78+
.then(async (res) => {
79+
if (res.status && executeConfig) {
80+
wrapWithToast(async () => await executeRuling(executeConfig.request), publicClient);
81+
}
82+
})
83+
.finally(() => setIsSending(false));
84+
} else if (executeConfig) {
85+
wrapWithToast(async () => await executeRuling(executeConfig.request), publicClient).finally(() =>
86+
setIsSending(false)
87+
);
88+
}
89+
}, [publicClient, executeConfig, manualModeConfig, arbitrableSettings, changeToManualMode, executeRuling]);
2990

3091
return (
3192
<Container>
@@ -48,7 +109,12 @@ const ManualRuling: React.FC = () => {
48109
/>
49110
</SelectContainer>
50111

51-
<Button text="Rule" />
112+
<Button
113+
text="Rule"
114+
onClick={handleRuling}
115+
isLoading={isLoadingExecuteConfig || isSending}
116+
disabled={isDisabled || isError || isSending}
117+
/>
52118
</Container>
53119
);
54120
};

0 commit comments

Comments
 (0)