Skip to content

Commit 1277bec

Browse files
authoredJun 23, 2023
Merge branch 'dev' into fix(web)/fix-links-and-modal-behavior
2 parents 173a2a5 + 5acd04a commit 1277bec

File tree

34 files changed

+3028
-3930
lines changed

34 files changed

+3028
-3930
lines changed
 

‎.github/workflows/deploy-subgraph.yml

+28-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ on:
1111
options:
1212
- arbitrum-goerli
1313
- arbitrum
14+
update:
15+
description: Whether to update the subgraph with the current artifacts for the selected network.
16+
required: true
17+
default: true
18+
type: boolean
1419

1520
permissions:
1621
contents: read
@@ -33,16 +38,38 @@ jobs:
3338
with:
3439
node-version: 16
3540

41+
- name: Install Yarn if running locally
42+
if: ${{ env.ACT }}
43+
run: npm install -g yarn
44+
3645
- name: Install the dependencies
3746
run: yarn install
3847

48+
- name: Install jq and yq
49+
if: ${{ inputs.update }}
50+
run: |
51+
mkdir bin
52+
wget -qO bin/jq https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64
53+
wget -qO bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
54+
chmod a+x bin/jq bin/yq
55+
56+
- name: Update the subgraph definition
57+
if: ${{ inputs.update }}
58+
run: |
59+
export PATH=$PWD/../bin:$PATH
60+
yarn update:${{ inputs.network }}
61+
working-directory: subgraph
62+
3963
- name: Build the subgraph
4064
run: |
4165
yarn codegen
4266
yarn build
43-
67+
working-directory: subgraph
68+
4469
- name: Authenticate with TheGraph
4570
run: yarn graph auth "${{ secrets.SUBGRAPH_AUTH_TOKEN }}" --product hosted-service
71+
working-directory: subgraph
4672

4773
- name: Deploy the subgraph
4874
run: yarn deploy:${{ inputs.network }}
75+
working-directory: subgraph

‎.github/workflows/scorecards.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,6 @@ jobs:
8484

8585
# Upload the results to GitHub's code scanning dashboard.
8686
- name: "Upload to code-scanning"
87-
uses: github/codeql-action/upload-sarif@807578363a7869ca324a79039e6db9c843e0e100 # v2.1.27
87+
uses: github/codeql-action/upload-sarif@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
8888
with:
8989
sarif_file: results.sarif

‎contracts/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,19 @@
4141
"@nomicfoundation/hardhat-chai-matchers": "^1.0.6",
4242
"@nomiclabs/hardhat-ethers": "^2.2.3",
4343
"@nomiclabs/hardhat-solhint": "^3.0.1",
44-
"@openzeppelin/contracts": "^4.9.1",
44+
"@openzeppelin/contracts": "^4.9.2",
4545
"@typechain/ethers-v5": "^11.0.0",
4646
"@typechain/hardhat": "^7.0.0",
4747
"@types/chai": "^4.3.5",
4848
"@types/mocha": "^10.0.1",
4949
"@types/node": "^16.18.36",
5050
"chai": "^4.3.7",
51-
"dotenv": "^16.1.4",
51+
"dotenv": "^16.3.1",
5252
"ethereumjs-util": "^7.1.5",
5353
"ethers": "^5.7.2",
5454
"hardhat": "^2.15.0",
5555
"hardhat-contract-sizer": "^2.10.0",
56-
"hardhat-deploy": "^0.11.30",
56+
"hardhat-deploy": "^0.11.31",
5757
"hardhat-deploy-ethers": "^0.4.0-next.1",
5858
"hardhat-deploy-tenderly": "^0.2.0",
5959
"hardhat-docgen": "^1.3.0",

‎prettier-config/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"main": "index.js",
55
"license": "MIT",
66
"dependencies": {
7-
"eslint": "^8.42.0",
7+
"eslint": "^8.43.0",
88
"prettier": "^2.8.8",
99
"prettier-plugin-solidity": "^1.1.3"
1010
},

‎scripts/act-subgraph.yml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
3+
act workflow_dispatch -j buildAndDeploy --input network=arbitrum-goerli,update=true

‎scripts/check-prerequisites.sh

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,22 @@ function require() #cmd
1111
fi
1212
}
1313

14+
# for the NPM and toolchain version management
15+
require volta
16+
17+
# for the local subgraph node
1418
require docker
1519
require docker-compose
20+
21+
# for some contracts utilities
22+
require cast
23+
require forge
24+
25+
# for the subgraph and contracts utilities
1626
require jq
1727
require yq
18-
require volta
28+
29+
# for the local testing of Github Actions
30+
require act
1931

2032
exit $status

‎web/package.json

-3
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@
6565
"@sentry/react": "^7.55.2",
6666
"@sentry/tracing": "^7.55.2",
6767
"@types/react-modal": "^3.16.0",
68-
"@web3-react/core": "^6.1.9",
69-
"@web3-react/injected-connector": "^6.0.7",
70-
"@web3-react/types": "^6.0.7",
7168
"@web3modal/ethereum": "^2.2.2",
7269
"@web3modal/react": "^2.2.2",
7370
"chart.js": "^3.9.1",

‎web/src/app.tsx

+10-18
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ import { Routes, Route } from "react-router-dom";
55
import "react-loading-skeleton/dist/skeleton.css";
66
import Web3Provider from "context/Web3Provider";
77
import StyledComponentsProvider from "context/StyledComponentsProvider";
8-
import WrongChainBoundary from "components/WrongChainBoundary";
98
import Layout from "layout/index";
109
import Home from "./pages/Home";
1110
import Cases from "./pages/Cases";
1211
import Dashboard from "./pages/Dashboard";
1312
import Courts from "./pages/Courts";
14-
1513
import "react-toastify/dist/ReactToastify.css";
1614

1715
const fetcherBuilder =
@@ -27,26 +25,20 @@ const App: React.FC = () => {
2725
<SWRConfig
2826
value={{
2927
fetcher: fetcherBuilder(
30-
process.env.REACT_APP_SUBGRAPH_ENDPOINT ??
31-
"https://api.thegraph.com/subgraphs/name/alcercu/kleroscoretest"
28+
process.env.REACT_APP_SUBGRAPH_ENDPOINT ?? "https://api.thegraph.com/subgraphs/name/alcercu/kleroscoretest"
3229
),
3330
}}
3431
>
3532
<Web3Provider>
36-
<WrongChainBoundary>
37-
<Routes>
38-
<Route path="/" element={<Layout />}>
39-
<Route index element={<Home />} />
40-
<Route path="cases/*" element={<Cases />} />
41-
<Route path="courts/*" element={<Courts />} />
42-
<Route path="dashboard" element={<Dashboard />} />
43-
<Route
44-
path="*"
45-
element={<h1>Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯</h1>}
46-
/>
47-
</Route>
48-
</Routes>
49-
</WrongChainBoundary>
33+
<Routes>
34+
<Route path="/" element={<Layout />}>
35+
<Route index element={<Home />} />
36+
<Route path="cases/*" element={<Cases />} />
37+
<Route path="courts/*" element={<Courts />} />
38+
<Route path="dashboard" element={<Dashboard />} />
39+
<Route path="*" element={<h1>Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯</h1>} />
40+
</Route>
41+
</Routes>
5042
</Web3Provider>
5143
</SWRConfig>
5244
</StyledComponentsProvider>
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
11
import React from "react";
22
import styled from "styled-components";
3-
import { useAccount, useNetwork } from "wagmi";
4-
import { arbitrumGoerli } from "wagmi/chains";
3+
import { useAccount, useNetwork, useSwitchNetwork } from "wagmi";
54
import { useWeb3Modal } from "@web3modal/react";
65
import { shortenAddress } from "utils/shortenAddress";
76
import { Button } from "@kleros/ui-components-library";
8-
9-
const AccountDisplay: React.FC = () => {
10-
return (
11-
<StyledContainer>
12-
<ChainDisplay />
13-
<AddressDisplay />
14-
</StyledContainer>
15-
);
16-
};
17-
18-
export const ChainDisplay: React.FC = () => {
19-
const { chain } = useNetwork();
20-
return <small>{chain?.name}</small>;
21-
};
22-
23-
export const AddressDisplay: React.FC = () => {
24-
const { address } = useAccount();
25-
return <label>{address && shortenAddress(address)}</label>;
26-
};
7+
import { SUPPORTED_CHAINS, DEFAULT_CHAIN } from "consts/chains";
278

289
const StyledContainer = styled.div`
2910
width: fit-content;
@@ -46,15 +27,63 @@ const StyledContainer = styled.div`
4627
}
4728
`;
4829

30+
const AccountDisplay: React.FC = () => {
31+
return (
32+
<StyledContainer>
33+
<ChainDisplay />
34+
<AddressDisplay />
35+
</StyledContainer>
36+
);
37+
};
38+
39+
export const ChainDisplay: React.FC = () => {
40+
const { chain } = useNetwork();
41+
return <small>{chain?.name}</small>;
42+
};
43+
44+
export const AddressDisplay: React.FC = () => {
45+
const { address } = useAccount();
46+
return <label>{address && shortenAddress(address)}</label>;
47+
};
48+
49+
export const SwitchChainButton: React.FC = () => {
50+
const { switchNetwork, isLoading } = useSwitchNetwork();
51+
const handleSwitch = () => {
52+
if (!switchNetwork) {
53+
console.error("Cannot switch network. Please do it manually.");
54+
return;
55+
}
56+
try {
57+
switchNetwork(DEFAULT_CHAIN);
58+
} catch (err) {
59+
console.error(err);
60+
}
61+
};
62+
return (
63+
<Button
64+
isLoading={isLoading}
65+
disabled={isLoading}
66+
text={`Switch to ${SUPPORTED_CHAINS[DEFAULT_CHAIN].chainName}`}
67+
onClick={handleSwitch}
68+
/>
69+
);
70+
};
71+
4972
const ConnectButton: React.FC = () => {
50-
const { isConnected } = useAccount();
51-
const { open, setDefaultChain, isOpen } = useWeb3Modal();
52-
setDefaultChain(arbitrumGoerli);
53-
return isConnected ? (
54-
<AccountDisplay />
55-
) : (
73+
const { open, isOpen } = useWeb3Modal();
74+
return (
5675
<Button disabled={isOpen} small text={"Connect"} onClick={async () => await open({ route: "ConnectWallet" })} />
5776
);
5877
};
5978

60-
export default ConnectButton;
79+
const ConnectWallet: React.FC = () => {
80+
const { chain } = useNetwork();
81+
const { isConnected } = useAccount();
82+
if (isConnected) {
83+
if (chain && chain.id !== DEFAULT_CHAIN) {
84+
return <SwitchChainButton />;
85+
} else return <AccountDisplay />;
86+
} else return <ConnectButton />;
87+
};
88+
89+
export default ConnectWallet;

‎web/src/components/DisputeCard/index.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ const StyledCard = styled(Card)`
1616
min-width: 312px;
1717
width: auto;
1818
height: 260px;
19+
20+
.react-loading-skeleton {
21+
z-index: 0;
22+
}
1923
`;
2024

2125
const Container = styled.div`

‎web/src/components/EnsureChain.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from "react";
2+
import { DEFAULT_CHAIN } from "consts/chains";
3+
import { useNetwork } from "wagmi";
4+
import ConnectWallet from "components/ConnectWallet";
5+
6+
interface IEnsureChain {
7+
children: React.ReactElement;
8+
}
9+
10+
export const EnsureChain: React.FC<IEnsureChain> = ({ children }) => {
11+
const { chain } = useNetwork();
12+
13+
return chain && chain.id === DEFAULT_CHAIN ? children : <ConnectWallet />;
14+
};

‎web/src/components/Overlay.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import styled from "styled-components";
2+
3+
export const Overlay = styled.div`
4+
position: fixed;
5+
top: 0;
6+
left: 0;
7+
width: 100vw;
8+
height: 100vh;
9+
background-color: ${({ theme }) => theme.blackLowOpacity};
10+
z-index: 1;
11+
`;

‎web/src/components/WrongChainBoundary.tsx

-46
This file was deleted.

‎web/src/connectors/injected.ts

-6
This file was deleted.

‎web/src/hooks/useClassicAppealContext.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useMemo, useState, createContext, useContext } from "react";
22
import { useParams } from "react-router-dom";
33
import { ONE_BASIS_POINT } from "consts/index";
44
import { Periods } from "consts/periods";
5-
import { notUndefined } from "utils/index";
5+
import { isUndefined } from "utils/index";
66
import { useGetMetaEvidence } from "queries/useGetMetaEvidence";
77
import { useAppealCost } from "queries/useAppealCost";
88
import { useDisputeKitClassicMultipliers } from "queries/useDisputeKitClassicMultipliers";
@@ -110,12 +110,12 @@ const getWinningChoice = (dispute?: ClassicAppealQuery["dispute"]) => {
110110
};
111111

112112
const getLoserRequiredFunding = (appealCost: bigint, loser_stake_multiplier: bigint): bigint =>
113-
notUndefined([appealCost, loser_stake_multiplier])
113+
!isUndefined(appealCost) && !isUndefined(loser_stake_multiplier)
114114
? appealCost + (loser_stake_multiplier * appealCost) / ONE_BASIS_POINT
115115
: 0n;
116116

117117
const getWinnerRequiredFunding = (appealCost: bigint, winner_stake_multiplier: bigint): bigint =>
118-
notUndefined([appealCost, winner_stake_multiplier])
118+
!isUndefined(appealCost) && !isUndefined(winner_stake_multiplier)
119119
? appealCost + (winner_stake_multiplier * appealCost) / ONE_BASIS_POINT
120120
: 0n;
121121

@@ -130,7 +130,7 @@ const getDeadline = (lastPeriodChange: string, appealPeriodDuration: string, los
130130
function useLoserSideCountdown(lastPeriodChange: string, appealPeriodDuration: string, loserTimeMultiplier: string) {
131131
const deadline = useMemo(
132132
() =>
133-
notUndefined([lastPeriodChange, appealPeriodDuration, loserTimeMultiplier])
133+
!isUndefined(lastPeriodChange) && !isUndefined(appealPeriodDuration) && !isUndefined(loserTimeMultiplier)
134134
? getDeadline(lastPeriodChange, appealPeriodDuration, loserTimeMultiplier)
135135
: 0,
136136
[lastPeriodChange, appealPeriodDuration, loserTimeMultiplier]

‎web/src/hooks/useConnect.ts

-46
This file was deleted.

‎web/src/hooks/useWeb3.ts

-3
This file was deleted.

‎web/src/layout/Header/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useFocusOutside } from "hooks/useFocusOutside";
99

1010
const Container = styled.div`
1111
position: sticky;
12-
z-index: 9999;
12+
z-index: 1;
1313
top: 0;
1414
width: 100%;
1515
height: 64px;

‎web/src/layout/Header/navbar/DappList.tsx

+1-10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Linguo from "svgs/icons/linguo.svg";
1010
import POH from "svgs/icons/poh-image.png";
1111
import Tokens from "svgs/icons/tokens.svg";
1212
import Product from "./Product";
13+
import { Overlay } from "components/Overlay";
1314

1415
interface IDappList {
1516
toggleSolution: () => void;
@@ -104,16 +105,6 @@ const ItemsDiv = styled.div`
104105
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
105106
`;
106107

107-
const Overlay = styled.div`
108-
position: fixed;
109-
top: 0;
110-
left: 0;
111-
width: 100vw;
112-
height: 100vh;
113-
background-color: ${({ theme }) => theme.blackLowOpacity};
114-
z-index: 1;
115-
`;
116-
117108
const DappList: React.FC<IDappList> = ({ toggleSolution }) => {
118109
const containerRef = useRef(null);
119110
useFocusOutside(containerRef, () => {

‎web/src/layout/Header/navbar/Menu/Help.tsx

+1-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Bug from "svgs/icons/bug.svg";
77
import ETH from "svgs/icons/eth.svg";
88
import Faq from "svgs/menu-icons/help.svg";
99
import Telegram from "svgs/socialmedia/telegram.svg";
10+
import { Overlay } from "components/Overlay";
1011

1112
const ITEMS = [
1213
{
@@ -85,16 +86,6 @@ const Icon = styled.svg`
8586
fill: ${({ theme }) => theme.secondaryPurple};
8687
`;
8788

88-
const Overlay = styled.div`
89-
position: fixed;
90-
top: 0;
91-
left: 0;
92-
width: 100vw;
93-
height: 100vh;
94-
background-color: ${({ theme }) => theme.blackLowOpacity};
95-
z-index: 1;
96-
`;
97-
9889
const Help: React.FC<IHelp> = ({ toggle }) => {
9990
const containerRef = useRef(null);
10091
useFocusOutside(containerRef, () => {

‎web/src/layout/Header/navbar/Menu/Settings/General.tsx

+23-22
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import React from "react";
22
import styled from "styled-components";
3+
import { useAccount } from "wagmi";
34
import Identicon from "react-identicons";
4-
import ConnectButton, { AddressDisplay, ChainDisplay } from "components/ConnectButton";
5-
import { useWeb3 } from "hooks/useWeb3";
5+
import { AddressDisplay, ChainDisplay } from "components/ConnectWallet";
6+
import { EnsureChain } from "components/EnsureChain";
67

78
const Container = styled.div`
89
display: flex;
@@ -42,33 +43,33 @@ const StyledIdenticon = styled.div`
4243
margin-top: 32px;
4344
`;
4445

45-
const StyledConnectButtonContainer = styled.div`
46+
const EnsureChainContainer = styled.div`
4647
display: flex;
4748
justify-content: center;
4849
padding: 16px;
4950
`;
5051

5152
const General: React.FC = () => {
52-
const { account } = useWeb3();
53+
const { address } = useAccount();
5354

54-
return account ? (
55-
<Container>
56-
<StyledChainContainer>
57-
<ChainDisplay />
58-
</StyledChainContainer>
59-
{account && (
60-
<StyledIdenticon>
61-
<Identicon size="24" string={account} />
62-
</StyledIdenticon>
63-
)}
64-
<StyledAddressContainer>
65-
<AddressDisplay />
66-
</StyledAddressContainer>
67-
</Container>
68-
) : (
69-
<StyledConnectButtonContainer>
70-
<ConnectButton />
71-
</StyledConnectButtonContainer>
55+
return (
56+
<EnsureChainContainer>
57+
<EnsureChain>
58+
<Container>
59+
<StyledChainContainer>
60+
<ChainDisplay />
61+
</StyledChainContainer>
62+
{address && (
63+
<StyledIdenticon>
64+
<Identicon size="24" string={address} />
65+
</StyledIdenticon>
66+
)}
67+
<StyledAddressContainer>
68+
<AddressDisplay />
69+
</StyledAddressContainer>
70+
</Container>
71+
</EnsureChain>
72+
</EnsureChainContainer>
7273
);
7374
};
7475

‎web/src/layout/Header/navbar/Menu/Settings/SendMeNotifications/FormNotifs/index.tsx

-8
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ const FormEmailContainer = styled.div`
2525
position: relative;
2626
`;
2727

28-
const EmailErrorContainer = styled.div`
29-
position: absolute;
30-
color: ${({ theme }) => theme.error};
31-
font-size: 12px;
32-
padding-top: 4px;
33-
padding-left: 16px;
34-
`;
35-
3628
const OPTIONS = [{ label: "When x." }, { label: "When y." }, { label: "When z." }, { label: "When w." }];
3729

3830
const FormNotifs: React.FC = () => {

‎web/src/layout/Header/navbar/Menu/Settings/index.tsx

+1-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Tabs } from "@kleros/ui-components-library";
44
import General from "./General";
55
import SendMeNotifications from "./SendMeNotifications";
66
import { useFocusOutside } from "hooks/useFocusOutside";
7+
import { Overlay } from "components/Overlay";
78

89
const Container = styled.div`
910
display: flex;
@@ -32,16 +33,6 @@ const StyledTabs = styled(Tabs)`
3233
width: calc(300px + (424 - 300) * ((100vw - 300px) / (1250 - 300)));
3334
`;
3435

35-
const Overlay = styled.div`
36-
position: fixed;
37-
top: 0;
38-
left: 0;
39-
width: 100vw;
40-
height: 100vh;
41-
background-color: ${({ theme }) => theme.blackLowOpacity};
42-
z-index: 1;
43-
`;
44-
4536
const TABS = [
4637
{
4738
text: "General",

‎web/src/layout/Header/navbar/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import styled from "styled-components";
33
import { useLockBodyScroll, useToggle } from "react-use";
4-
import ConnectButton from "components/ConnectButton";
4+
import ConnectWallet from "components/ConnectWallet";
55
import LightButton from "components/LightButton";
66
import KlerosSolutionsIcon from "svgs/menu-icons/kleros-solutions.svg";
77
import { useOpenContext } from "../index";
@@ -52,7 +52,7 @@ const NavBar: React.FC = () => {
5252
<hr />
5353
<Explore />
5454
<hr />
55-
<ConnectButton />
55+
<ConnectWallet />
5656
<hr />
5757
<Menu />
5858
</Container>

‎web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx

+45-25
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ import { useParams } from "react-router-dom";
44
import { useAccount, useBalance } from "wagmi";
55
import { useDebounce } from "react-use";
66
import { Field, Button } from "@kleros/ui-components-library";
7-
import { usePrepareDisputeKitClassicFundAppeal, useDisputeKitClassicFundAppeal } from "hooks/contracts/generated";
87
import { wrapWithToast } from "utils/wrapWithToast";
8+
import { isUndefined } from "utils/index";
9+
import { EnsureChain } from "components/EnsureChain";
10+
import { usePrepareDisputeKitClassicFundAppeal, useDisputeKitClassicFundAppeal } from "hooks/contracts/generated";
911
import { useParsedAmount } from "hooks/useParsedAmount";
1012
import {
1113
useLoserSideCountdownContext,
1214
useSelectedOptionContext,
1315
useFundingContext,
1416
} from "hooks/useClassicAppealContext";
15-
import { isUndefined } from "utils/index";
1617

17-
const Fund: React.FC = () => {
18+
const useNeedFund = () => {
1819
const loserSideCountdown = useLoserSideCountdownContext();
1920
const { fundedChoices, winningChoice } = useFundingContext();
2021
const needFund =
@@ -23,24 +24,41 @@ const Fund: React.FC = () => {
2324
!isUndefined(winningChoice) &&
2425
fundedChoices.length > 0 &&
2526
!fundedChoices.includes(winningChoice));
27+
28+
return needFund;
29+
};
30+
31+
const useFundAppeal = (parsedAmount) => {
2632
const { id } = useParams();
33+
const { selectedOption } = useSelectedOptionContext();
34+
const { config: fundAppealConfig } = usePrepareDisputeKitClassicFundAppeal({
35+
enabled: !isUndefined(id) && !isUndefined(selectedOption),
36+
args: [BigInt(id ?? 0), BigInt(selectedOption ?? 0)],
37+
value: parsedAmount,
38+
});
39+
40+
const { writeAsync: fundAppeal } = useDisputeKitClassicFundAppeal(fundAppealConfig);
41+
42+
return fundAppeal;
43+
};
44+
45+
const Fund: React.FC = () => {
46+
const needFund = useNeedFund();
2747
const { address, isDisconnected } = useAccount();
2848
const { data: balance } = useBalance({
2949
address,
3050
watch: true,
3151
});
52+
3253
const [amount, setAmount] = useState("");
3354
const [debouncedAmount, setDebouncedAmount] = useState("");
3455
useDebounce(() => setDebouncedAmount(amount), 500, [amount]);
56+
3557
const parsedAmount = useParsedAmount(debouncedAmount);
58+
3659
const [isSending, setIsSending] = useState(false);
37-
const { selectedOption } = useSelectedOptionContext();
38-
const { config: fundAppealConfig } = usePrepareDisputeKitClassicFundAppeal({
39-
enabled: !isUndefined(id) && !isUndefined(selectedOption),
40-
args: [BigInt(id ?? 0), BigInt(selectedOption ?? 0)],
41-
value: parsedAmount,
42-
});
43-
const { writeAsync: fundAppeal } = useDisputeKitClassicFundAppeal(fundAppealConfig);
60+
const fundAppeal = useFundAppeal(parsedAmount);
61+
4462
return needFund ? (
4563
<div>
4664
<label>How much ETH do you want to contribute?</label>
@@ -53,21 +71,23 @@ const Fund: React.FC = () => {
5371
}}
5472
placeholder="Amount to fund"
5573
/>
56-
<StyledButton
57-
disabled={isDisconnected || isSending || !balance || parsedAmount > balance.value}
58-
text={isDisconnected ? "Connect to Fund" : "Fund"}
59-
onClick={() => {
60-
if (fundAppeal) {
61-
setIsSending(true);
62-
wrapWithToast(fundAppeal!())
63-
.then(() => {
64-
setAmount("");
65-
close();
66-
})
67-
.finally(() => setIsSending(false));
68-
}
69-
}}
70-
/>
74+
<EnsureChain>
75+
<StyledButton
76+
disabled={isDisconnected || isSending || !balance || parsedAmount > balance.value}
77+
text={isDisconnected ? "Connect to Fund" : "Fund"}
78+
onClick={() => {
79+
if (fundAppeal) {
80+
setIsSending(true);
81+
wrapWithToast(fundAppeal())
82+
.then(() => {
83+
setAmount("");
84+
close();
85+
})
86+
.finally(() => setIsSending(false));
87+
}
88+
}}
89+
/>
90+
</EnsureChain>
7191
</div>
7292
</div>
7393
) : (

‎web/src/pages/Cases/CaseDetails/Appeal/Classic/Options/index.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ import styled from "styled-components";
33
import { useLoserSideCountdownContext } from "hooks/useClassicAppealContext";
44
import StageOne from "./StageOne";
55
import StageTwo from "./StageTwo";
6-
import { notUndefined } from "utils/index";
6+
import { isUndefined } from "utils/index";
77

88
const Options: React.FC = () => {
99
const loserSideCountdown = useLoserSideCountdownContext();
10-
return notUndefined(loserSideCountdown) ? (
11-
<Container>
12-
{loserSideCountdown! > 0 ? <StageOne /> : <StageTwo />}
13-
</Container>
10+
return !isUndefined(loserSideCountdown) ? (
11+
<Container>{loserSideCountdown > 0 ? <StageOne /> : <StageTwo />}</Container>
1412
) : (
1513
<h1>Loading...</h1>
1614
);

‎web/src/pages/Cases/CaseDetails/Evidence/SubmitEvidenceModal.tsx

+30-27
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Textarea, Button } from "@kleros/ui-components-library";
66
import { wrapWithToast, OPTIONS as toastOptions } from "utils/wrapWithToast";
77
import { uploadFormDataToIPFS } from "utils/uploadFormDataToIPFS";
88
import { useWalletClient } from "wagmi";
9+
import { EnsureChain } from "components/EnsureChain";
910
import { prepareWriteDisputeKitClassic } from "hooks/contracts/generated";
1011

1112
const SubmitEvidenceModal: React.FC<{
@@ -22,33 +23,35 @@ const SubmitEvidenceModal: React.FC<{
2223
<StyledTextArea value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Your Arguments" />
2324
<ButtonArea>
2425
<Button variant="secondary" disabled={isSending} text="Return" onClick={close} />
25-
<Button
26-
text="Submit"
27-
isLoading={isSending}
28-
disabled={isSending}
29-
onClick={() => {
30-
setIsSending(true);
31-
const formData = constructEvidence(message);
32-
toast.info("Uploading to IPFS", toastOptions);
33-
uploadFormDataToIPFS(formData)
34-
.then(async (res) => {
35-
const response = await res.json();
36-
if (res.status === 200 && walletClient) {
37-
const cid = "/ipfs/" + response["cid"];
38-
const { request } = await prepareWriteDisputeKitClassic({
39-
functionName: "submitEvidence",
40-
args: [BigInt(evidenceGroup), cid],
41-
});
42-
await wrapWithToast(walletClient.writeContract(request)).then(() => {
43-
setMessage("");
44-
close();
45-
});
46-
}
47-
})
48-
.catch()
49-
.finally(() => setIsSending(false));
50-
}}
51-
/>
26+
<EnsureChain>
27+
<Button
28+
text="Submit"
29+
isLoading={isSending}
30+
disabled={isSending}
31+
onClick={() => {
32+
setIsSending(true);
33+
const formData = constructEvidence(message);
34+
toast.info("Uploading to IPFS", toastOptions);
35+
uploadFormDataToIPFS(formData)
36+
.then(async (res) => {
37+
const response = await res.json();
38+
if (res.status === 200 && walletClient) {
39+
const cid = "/ipfs/" + response["cid"];
40+
const { request } = await prepareWriteDisputeKitClassic({
41+
functionName: "submitEvidence",
42+
args: [BigInt(evidenceGroup), cid],
43+
});
44+
await wrapWithToast(walletClient.writeContract(request)).then(() => {
45+
setMessage("");
46+
close();
47+
});
48+
}
49+
})
50+
.catch()
51+
.finally(() => setIsSending(false));
52+
}}
53+
/>
54+
</EnsureChain>
5255
</ButtonArea>
5356
</StyledModal>
5457
);

‎web/src/pages/Cases/CaseDetails/Evidence/index.tsx

+26-32
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ import { useEvidenceGroup } from "queries/useEvidenceGroup";
77
import { useEvidences } from "queries/useEvidences";
88
import SubmitEvidenceModal from "./SubmitEvidenceModal";
99
import EvidenceCard from "components/EvidenceCard";
10+
import { EnsureChain } from "components/EnsureChain";
11+
12+
const Container = styled.div`
13+
width: 100%;
14+
display: flex;
15+
flex-direction: column;
16+
gap: 16px;
17+
18+
align-items: center;
19+
`;
20+
21+
const StyledButton = styled(Button)`
22+
align-self: flex-end;
23+
`;
1024

1125
const Evidence: React.FC<{ arbitrable?: string }> = ({ arbitrable }) => {
1226
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -17,44 +31,24 @@ const Evidence: React.FC<{ arbitrable?: string }> = ({ arbitrable }) => {
1731
return (
1832
<Container>
1933
{evidenceGroup && (
20-
<SubmitEvidenceModal
21-
isOpen={isModalOpen}
22-
close={() => setIsModalOpen(false)}
23-
{...{ evidenceGroup }}
24-
/>
34+
<SubmitEvidenceModal isOpen={isModalOpen} close={() => setIsModalOpen(false)} {...{ evidenceGroup }} />
2535
)}
2636
<Searchbar />
27-
<StyledButton
28-
small
29-
text="Submit Evidence"
30-
disabled={typeof address === "undefined" || isModalOpen}
31-
isLoading={isModalOpen}
32-
onClick={() => setIsModalOpen(true)}
33-
/>
37+
<EnsureChain>
38+
<StyledButton
39+
small
40+
text="Submit Evidence"
41+
disabled={typeof address === "undefined" || isModalOpen}
42+
isLoading={isModalOpen}
43+
onClick={() => setIsModalOpen(true)}
44+
/>
45+
</EnsureChain>
3446
{data &&
35-
data.evidences.map(({ evidence, sender }, i) => (
36-
<EvidenceCard
37-
key={i}
38-
index={i + 1}
39-
sender={sender?.id}
40-
{...{ evidence }}
41-
/>
47+
data.evidences.map(({ id, evidence, sender }, i) => (
48+
<EvidenceCard key={id} index={i + 1} sender={sender?.id} {...{ evidence }} />
4249
))}
4350
</Container>
4451
);
4552
};
4653

47-
const Container = styled.div`
48-
width: 100%;
49-
display: flex;
50-
flex-direction: column;
51-
gap: 16px;
52-
53-
align-items: center;
54-
`;
55-
56-
const StyledButton = styled(Button)`
57-
align-self: flex-end;
58-
`;
59-
6054
export default Evidence;

‎web/src/pages/Cases/CaseDetails/Voting/Binary.tsx

+38-42
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Button, Textarea } from "@kleros/ui-components-library";
55
import { useGetMetaEvidence } from "queries/useGetMetaEvidence";
66
import { wrapWithToast } from "utils/wrapWithToast";
77
import { useWalletClient } from "wagmi";
8+
import { EnsureChain } from "components/EnsureChain";
89
import { prepareWriteDisputeKitClassic } from "hooks/contracts/generated";
910

1011
const Binary: React.FC<{ arbitrable?: string; voteIDs: string[] }> = ({ arbitrable, voteIDs }) => {
@@ -17,6 +18,21 @@ const Binary: React.FC<{ arbitrable?: string; voteIDs: string[] }> = ({ arbitrab
1718
const [justification, setJustification] = useState("");
1819
const { data: walletClient } = useWalletClient();
1920

21+
const handleVote = async (voteOption: number) => {
22+
setIsSending(true);
23+
setChosenOption(voteOption);
24+
const { request } = await prepareWriteDisputeKitClassic({
25+
functionName: "castVote",
26+
args: [parsedDisputeID, parsedVoteIDs, BigInt(voteOption), 0n, justification],
27+
});
28+
if (walletClient) {
29+
wrapWithToast(walletClient.writeContract(request)).finally(() => {
30+
setChosenOption(-1);
31+
setIsSending(false);
32+
});
33+
}
34+
};
35+
2036
return id ? (
2137
<Container>
2238
<MainContainer>
@@ -31,57 +47,37 @@ const Binary: React.FC<{ arbitrable?: string; voteIDs: string[] }> = ({ arbitrab
3147
variant="info"
3248
/>
3349
<OptionsContainer>
34-
{metaEvidence?.rulingOptions?.titles?.map((answer: string, i: number) => (
35-
<Button
36-
key={i}
37-
text={answer}
38-
disabled={isSending}
39-
isLoading={chosenOption === i + 1}
40-
onClick={async () => {
41-
setIsSending(true);
42-
setChosenOption(i + 1);
43-
const { request } = await prepareWriteDisputeKitClassic({
44-
functionName: "castVote",
45-
args: [parsedDisputeID, parsedVoteIDs, BigInt(i + 1), 0n, justification],
46-
});
47-
if (walletClient) {
48-
wrapWithToast(walletClient?.writeContract(request)).finally(() => {
49-
setChosenOption(-1);
50-
setIsSending(false);
51-
});
52-
}
53-
}}
54-
/>
55-
))}
50+
{metaEvidence?.rulingOptions?.titles?.map((answer: string, i: number) => {
51+
return (
52+
<EnsureChain key={answer}>
53+
<Button
54+
text={answer}
55+
disabled={isSending}
56+
isLoading={chosenOption === i + 1}
57+
onClick={() => handleVote(i + 1)}
58+
/>
59+
</EnsureChain>
60+
);
61+
})}
5662
</OptionsContainer>
5763
</MainContainer>
5864
<RefuseToArbitrateContainer>
59-
<Button
60-
variant="secondary"
61-
text="Refuse to Arbitrate"
62-
disabled={isSending}
63-
isLoading={chosenOption === 0}
64-
onClick={async () => {
65-
setIsSending(true);
66-
setChosenOption(0);
67-
const { request } = await prepareWriteDisputeKitClassic({
68-
functionName: "castVote",
69-
args: [parsedDisputeID, parsedVoteIDs, 0n, 0n, justification],
70-
});
71-
if (walletClient) {
72-
wrapWithToast(walletClient.writeContract(request)).finally(() => {
73-
setChosenOption(-1);
74-
setIsSending(false);
75-
});
76-
}
77-
}}
78-
/>
65+
<EnsureChain>
66+
<Button
67+
variant="secondary"
68+
text="Refuse to Arbitrate"
69+
disabled={isSending}
70+
isLoading={chosenOption === 0}
71+
onClick={() => handleVote(0)}
72+
/>
73+
</EnsureChain>
7974
</RefuseToArbitrateContainer>
8075
</Container>
8176
) : (
8277
<></>
8378
);
8479
};
80+
8581
const Container = styled.div`
8682
width: 100%;
8783
height: auto;

‎web/src/pages/Courts/CourtDetails/StakePanel/InputDisplay.tsx

+26-27
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,35 @@ import { useParams } from "react-router-dom";
44
import { formatEther } from "viem";
55
import { useDebounce } from "react-use";
66
import { useAccount } from "wagmi";
7-
import { Button, Field } from "@kleros/ui-components-library";
7+
import { Field } from "@kleros/ui-components-library";
88

99
import { useParsedAmount } from "hooks/useParsedAmount";
1010
import { usePNKBalance } from "queries/usePNKBalance";
1111
import { useJurorBalance } from "queries/useJurorBalance";
1212
import StakeWithdrawButton, { ActionType } from "./StakeWithdrawButton";
13+
import { EnsureChain } from "components/EnsureChain";
14+
15+
const StyledField = styled(Field)`
16+
width: 100%;
17+
height: fit-content;
18+
`;
19+
20+
const LabelArea = styled.div`
21+
display: flex;
22+
justify-content: space-between;
23+
`;
24+
25+
const StyledLabel = styled.label`
26+
color: ${({ theme }) => theme.primaryBlue};
27+
cursor: pointer;
28+
`;
29+
30+
const InputArea = styled.div`
31+
display: flex;
32+
flex-direction: column;
33+
align-items: center;
34+
gap: 12px;
35+
`;
1336

1437
interface IInputDisplay {
1538
action: ActionType;
@@ -57,7 +80,7 @@ const InputDisplay: React.FC<IInputDisplay> = ({ action, isSending, setIsSending
5780
}
5881
variant="info"
5982
/>
60-
{address ? (
83+
<EnsureChain>
6184
<StakeWithdrawButton
6285
{...{
6386
parsedAmount,
@@ -67,34 +90,10 @@ const InputDisplay: React.FC<IInputDisplay> = ({ action, isSending, setIsSending
6790
setIsSending,
6891
}}
6992
/>
70-
) : (
71-
<Button text="Connect to Stake" />
72-
)}
93+
</EnsureChain>
7394
</InputArea>
7495
</>
7596
);
7697
};
7798

7899
export default InputDisplay;
79-
80-
const StyledField = styled(Field)`
81-
width: 100%;
82-
height: fit-content;
83-
`;
84-
85-
const LabelArea = styled.div`
86-
display: flex;
87-
justify-content: space-between;
88-
`;
89-
90-
const StyledLabel = styled.label`
91-
color: ${({ theme }) => theme.primaryBlue};
92-
cursor: pointer;
93-
`;
94-
95-
const InputArea = styled.div`
96-
display: flex;
97-
flex-direction: column;
98-
align-items: center;
99-
gap: 12px;
100-
`;

‎web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx

+10-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, { useMemo } from "react";
22
import { useParams } from "react-router-dom";
33
import { useAccount } from "wagmi";
44
import { Button } from "@kleros/ui-components-library";
5-
import { usePNKAllowance } from "hooks/queries/usePNKAllowance";
65
import {
76
getKlerosCore,
87
useKlerosCoreSetStake,
@@ -12,8 +11,10 @@ import {
1211
usePreparePnkIncreaseAllowance,
1312
} from "hooks/contracts/generated";
1413
import { useJurorBalance } from "queries/useJurorBalance";
14+
import { usePNKAllowance } from "queries/usePNKAllowance";
1515
import { wrapWithToast } from "utils/wrapWithToast";
1616
import { isUndefined } from "utils/index";
17+
import { EnsureChain } from "components/EnsureChain";
1718

1819
export enum ActionType {
1920
allowance = "allowance",
@@ -104,12 +105,14 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({ parsedAmount, action, se
104105

105106
const { text, checkDisabled, onClick } = buttonProps[isAllowance ? ActionType.allowance : action];
106107
return (
107-
<Button
108-
text={text}
109-
isLoading={isSending}
110-
disabled={isSending || parsedAmount == 0n || !!isUndefined(targetStake) || checkDisabled()}
111-
onClick={onClick}
112-
/>
108+
<EnsureChain>
109+
<Button
110+
text={text}
111+
isLoading={isSending}
112+
disabled={isSending || parsedAmount == 0n || !!isUndefined(targetStake) || checkDisabled()}
113+
onClick={onClick}
114+
/>
115+
</EnsureChain>
113116
);
114117
};
115118

‎web/src/pages/Dashboard/JurorInfo/StakingRewards.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from "styled-components";
33
import { Box as _Box, Button } from "@kleros/ui-components-library";
44
import TokenRewards from "./TokenRewards";
55
import WithHelpTooltip from "../WithHelpTooltip";
6+
import { EnsureChain } from "components/EnsureChain";
67

78
const Container = styled.div`
89
display: flex;
@@ -33,7 +34,9 @@ const ClaimPNK: React.FC = () => {
3334
<label> Unclaimed: </label>
3435
<small> 1,000 PNK </small>
3536
</UnclaimedContainer>
36-
<Button small variant="tertiary" text="Claim" />
37+
<EnsureChain>
38+
<Button small variant="tertiary" text="Claim" />
39+
</EnsureChain>
3740
</Box>
3841
);
3942
};

‎web/src/utils/switchChain.ts

-51
This file was deleted.

‎yarn.lock

+2,668-3,489
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.