Skip to content

Commit 877bab9

Browse files
authored
Merge branch 'dev' into chore(contracts)/sol-version-update
2 parents d153ace + 27853d8 commit 877bab9

File tree

10 files changed

+107
-97
lines changed

10 files changed

+107
-97
lines changed

web/netlify/functions/uploadToIPFS.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { File, FilebaseClient } from "@filebase/client";
2-
import { Handler } from "@netlify/functions";
2+
import middy from "@middy/core";
33
import amqp, { Connection } from "amqplib";
44
import busboy from "busboy";
55

6-
const { FILEBASE_TOKEN, RABBITMQ_URL, FILEBASE_API_WRAPPER } = process.env;
6+
import { authMiddleware } from "../middleware/authMiddleware";
7+
8+
const { FILEBASE_TOKEN, RABBITMQ_URL } = process.env;
79
const filebase = new FilebaseClient({ token: FILEBASE_TOKEN ?? "" });
810

911
type FormElement =
@@ -65,24 +67,17 @@ const pinToFilebase = async (data: FormData, operation: string): Promise<Array<s
6567
return cids;
6668
};
6769

68-
export const handler: Handler = async (event) => {
70+
export const uploadToIPFS = async (event) => {
6971
const { queryStringParameters } = event;
7072

71-
if (!queryStringParameters || !queryStringParameters.key || !queryStringParameters.operation) {
73+
if (!queryStringParameters?.operation) {
7274
return {
7375
statusCode: 400,
7476
body: JSON.stringify({ message: "Invalid query parameters" }),
7577
};
7678
}
7779

78-
const { key, operation } = queryStringParameters;
79-
80-
if (key !== FILEBASE_API_WRAPPER) {
81-
return {
82-
statusCode: 403,
83-
body: JSON.stringify({ message: "Invalid API key" }),
84-
};
85-
}
80+
const { operation } = queryStringParameters;
8681

8782
try {
8883
const parsed = await parseMultipart(event);
@@ -102,3 +97,5 @@ export const handler: Handler = async (event) => {
10297
};
10398
}
10499
};
100+
101+
export const handler = middy(uploadToIPFS).use(authMiddleware());

web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"dependencies": {
8181
"@filebase/client": "^0.0.5",
8282
"@kleros/kleros-sdk": "workspace:^",
83-
"@kleros/ui-components-library": "^2.10.0",
83+
"@kleros/ui-components-library": "^2.12.0",
8484
"@lifi/widget": "^2.10.1",
8585
"@middy/core": "^5.3.2",
8686
"@middy/http-json-body-parser": "^5.3.2",

web/src/components/NumberInputField.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,19 @@ export const NumberInputField: React.FC<INumberInputField> = ({
5656
<Container {...{ className }}>
5757
{isEditing ? (
5858
<StyledField
59-
type="number"
59+
type="text"
60+
onInput={(e) => {
61+
const value = e.currentTarget.value.replace(/[^0-9.]/g, "");
62+
63+
e.currentTarget.value = formatter ? formatter(value) : value;
64+
return e;
65+
}}
6066
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
6167
onChange?.(event.target.value);
6268
}}
6369
onBlur={toggleEditing}
64-
{...{ value, placeholder, message, variant }}
70+
value={formatter ? formatter(value ?? "0") : value}
71+
{...{ placeholder, message, variant }}
6572
/>
6673
) : (
6774
<StyledField

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

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { prepareWriteEvidenceModule } from "hooks/contracts/generated";
1111
import { uploadFormDataToIPFS } from "utils/uploadFormDataToIPFS";
1212
import { wrapWithToast, OPTIONS as toastOptions } from "utils/wrapWithToast";
1313

14+
import { EnsureAuth } from "components/EnsureAuth";
1415
import { EnsureChain } from "components/EnsureChain";
1516

1617
const StyledModal = styled(Modal)`
@@ -67,35 +68,37 @@ const SubmitEvidenceModal: React.FC<{
6768
<ButtonArea>
6869
<Button variant="secondary" disabled={isSending} text="Return" onClick={close} />
6970
<EnsureChain>
70-
<Button
71-
text="Submit"
72-
isLoading={isSending}
73-
disabled={isSending}
74-
onClick={async () => {
75-
setIsSending(true);
76-
toast.info("Uploading to IPFS", toastOptions);
77-
const formData = await constructEvidence(message, file);
78-
uploadFormDataToIPFS(formData)
79-
.then(async (res) => {
80-
const response = await res.json();
81-
if (res.status === 200 && walletClient) {
82-
const cid = response["cids"][0];
83-
const { request } = await prepareWriteEvidenceModule({
84-
functionName: "submitEvidence",
85-
args: [BigInt(evidenceGroup), cid],
86-
});
87-
await wrapWithToast(async () => await walletClient.writeContract(request), publicClient).then(
88-
() => {
89-
setMessage("");
90-
close();
91-
}
92-
);
93-
}
94-
})
95-
.catch()
96-
.finally(() => setIsSending(false));
97-
}}
98-
/>
71+
<EnsureAuth>
72+
<Button
73+
text="Submit"
74+
isLoading={isSending}
75+
disabled={isSending}
76+
onClick={async () => {
77+
setIsSending(true);
78+
toast.info("Uploading to IPFS", toastOptions);
79+
const formData = await constructEvidence(message, file);
80+
uploadFormDataToIPFS(formData)
81+
.then(async (res) => {
82+
const response = await res.json();
83+
if (res.status === 200 && walletClient) {
84+
const cid = response["cids"][0];
85+
const { request } = await prepareWriteEvidenceModule({
86+
functionName: "submitEvidence",
87+
args: [BigInt(evidenceGroup), cid],
88+
});
89+
await wrapWithToast(async () => await walletClient.writeContract(request), publicClient).then(
90+
() => {
91+
setMessage("");
92+
close();
93+
}
94+
);
95+
}
96+
})
97+
.catch()
98+
.finally(() => setIsSending(false));
99+
}}
100+
/>
101+
</EnsureAuth>
99102
</EnsureChain>
100103
</ButtonArea>
101104
</StyledModal>

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useMemo } from "react";
1+
import React, { useState, useMemo, useEffect } from "react";
22
import styled from "styled-components";
33

44
import { useParams } from "react-router-dom";
@@ -68,11 +68,10 @@ const InputDisplay: React.FC<IInputDisplay> = ({
6868
setAmount,
6969
}) => {
7070
const [debouncedAmount, setDebouncedAmount] = useState("");
71+
const [errorMsg, setErrorMsg] = useState<string | undefined>();
7172
useDebounce(() => setDebouncedAmount(amount), 500, [amount]);
7273
const parsedAmount = useParsedAmount(uncommify(debouncedAmount) as `${number}`);
7374

74-
const [errorMsg, setErrorMsg] = useState<string | undefined>();
75-
7675
const { id } = useParams();
7776
const { address } = useAccount();
7877
const { data: balance } = usePnkBalanceOf({
@@ -89,6 +88,18 @@ const InputDisplay: React.FC<IInputDisplay> = ({
8988
const parsedStake = formatPNK(jurorBalance?.[2] || 0n, 0, true);
9089
const isStaking = useMemo(() => action === ActionType.stake, [action]);
9190

91+
useEffect(() => {
92+
if (parsedAmount > 0n && balance === 0n && isStaking) {
93+
setErrorMsg("You need a non-zero PNK balance to stake");
94+
} else if (isStaking && balance && parsedAmount > balance) {
95+
setErrorMsg("Insufficient balance to stake this amount");
96+
} else if (!isStaking && jurorBalance && parsedAmount > jurorBalance[2]) {
97+
setErrorMsg("Insufficient staked amount to withdraw this amount");
98+
} else {
99+
setErrorMsg(undefined);
100+
}
101+
}, [parsedAmount, isStaking, balance, jurorBalance]);
102+
92103
return (
93104
<>
94105
<LabelArea>
@@ -112,7 +123,7 @@ const InputDisplay: React.FC<IInputDisplay> = ({
112123
placeholder={isStaking ? "Amount to stake" : "Amount to withdraw"}
113124
message={errorMsg ?? undefined}
114125
variant={!isUndefined(errorMsg) ? "error" : "info"}
115-
formatter={(number: string) => commify(roundNumberDown(Number(number)))}
126+
formatter={(number: string) => (number !== "" ? commify(roundNumberDown(Number(number))) : "")}
116127
/>
117128
<EnsureChainContainer>
118129
<StakeWithdrawButton
@@ -123,7 +134,6 @@ const InputDisplay: React.FC<IInputDisplay> = ({
123134
isSending,
124135
setIsSending,
125136
setIsPopupOpen,
126-
setErrorMsg,
127137
}}
128138
/>
129139
</EnsureChainContainer>

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

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo, useEffect } from "react";
1+
import React, { useMemo } from "react";
22

33
import { useParams } from "react-router-dom";
44
import { useAccount, usePublicClient } from "wagmi";
@@ -34,7 +34,6 @@ interface IActionButton {
3434
setIsSending: (arg0: boolean) => void;
3535
setAmount: (arg0: string) => void;
3636
setIsPopupOpen: (arg0: boolean) => void;
37-
setErrorMsg: (arg0: string | undefined) => void;
3837
}
3938

4039
const StakeWithdrawButton: React.FC<IActionButton> = ({
@@ -43,7 +42,6 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({
4342
isSending,
4443
setIsSending,
4544
setIsPopupOpen,
46-
setErrorMsg,
4745
}) => {
4846
const { id } = useParams();
4947
const { address } = useAccount();
@@ -82,7 +80,7 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({
8280
return 0n;
8381
}, [jurorBalance, parsedAmount, isAllowance, isStaking]);
8482

85-
const { config: increaseAllowanceConfig, error: allowanceError } = usePreparePnkIncreaseAllowance({
83+
const { config: increaseAllowanceConfig } = usePreparePnkIncreaseAllowance({
8684
enabled: isAllowance && !isUndefined(klerosCore) && !isUndefined(targetStake) && !isUndefined(allowance),
8785
args: [klerosCore?.address, BigInt(targetStake ?? 0) - BigInt(allowance ?? 0)],
8886
});
@@ -99,7 +97,7 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({
9997
};
10098

10199
const { config: setStakeConfig, error: setStakeError } = usePrepareKlerosCoreSetStake({
102-
enabled: !isUndefined(targetStake) && !isUndefined(id) && !isAllowance,
100+
enabled: !isUndefined(targetStake) && !isUndefined(id) && !isAllowance && parsedAmount !== 0n,
103101
args: [BigInt(id ?? 0), targetStake],
104102
});
105103
const { writeAsync: setStake } = useKlerosCoreSetStake(setStakeConfig);
@@ -114,14 +112,6 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({
114112
}
115113
};
116114

117-
useEffect(() => {
118-
if (isAllowance) {
119-
setErrorMsg(allowanceError?.shortMessage);
120-
} else {
121-
setErrorMsg(setStakeError?.shortMessage);
122-
}
123-
}, [allowanceError, setStakeError, isAllowance, isStaking, setErrorMsg]);
124-
125115
const buttonProps = {
126116
[ActionType.allowance]: {
127117
text: "Allow PNK",
@@ -161,4 +151,4 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({
161151
);
162152
};
163153

164-
export default StakeWithdrawButton;
154+
export default StakeWithdrawButton;

web/src/pages/Courts/CourtDetails/index.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const CourtHeader = styled.h1`
3636
`;
3737

3838
const CourtInfo = styled.div`
39-
display: flex:
39+
display: flex;
4040
flex-direction: column;
4141
gap: 16px;
4242
@@ -71,13 +71,10 @@ const StyledCard = styled(Card)`
7171
`;
7272

7373
const StyledBreadcrumb = styled(Breadcrumb)`
74-
display: flex;
75-
margin-top: 12px;
7674
align-items: center;
77-
`;
78-
79-
const StyledBreadcrumbSkeleton = styled.div`
80-
margin-top: 12px;
75+
button {
76+
font-size: 16px;
77+
}
8178
`;
8279

8380
const CourtDetails: React.FC = () => {
@@ -102,13 +99,7 @@ const CourtDetails: React.FC = () => {
10299
<CourtHeader>
103100
<CourtInfo>
104101
{policy ? policy.name : <StyledSkeleton width={200} />}
105-
{items.length > 1 ? (
106-
<StyledBreadcrumb items={items} />
107-
) : (
108-
<StyledBreadcrumbSkeleton>
109-
<StyledSkeleton width={100} />
110-
</StyledBreadcrumbSkeleton>
111-
)}
102+
{items.length > 1 ? <StyledBreadcrumb items={items} /> : <StyledSkeleton width={100} />}
112103
</CourtInfo>
113104
<ButtonContainer>
114105
<HowItWorks

web/src/pages/Resolver/index.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { landscapeStyle } from "styles/landscapeStyle";
88
import { responsiveSize } from "styles/responsiveSize";
99

1010
import ConnectWallet from "components/ConnectWallet";
11+
import { EnsureAuth } from "components/EnsureAuth";
1112
import HeroImage from "components/HeroImage";
1213

1314
import Description from "./Briefing/Description";
@@ -41,6 +42,10 @@ const ConnectWalletContainer = styled.div`
4142
color: ${({ theme }) => theme.primaryText};
4243
`;
4344

45+
const StyledEnsureAuth = styled(EnsureAuth)`
46+
align-self: center;
47+
`;
48+
4449
const MiddleContentContainer = styled.div`
4550
display: flex;
4651
justify-content: center;
@@ -71,21 +76,23 @@ const DisputeResolver: React.FC = () => {
7176
<Container>
7277
{isConnected && !isPreviewPage ? <StyledLabel>Start a case</StyledLabel> : null}
7378
{isConnected ? (
74-
<MiddleContentContainer>
75-
{isConnected && !isPreviewPage ? <Timeline /> : null}
76-
<Routes>
77-
<Route index element={<Navigate to="title" replace />} />
78-
<Route path="/title/*" element={<Title />} />
79-
<Route path="/description/*" element={<Description />} />
80-
<Route path="/court/*" element={<Court />} />
81-
<Route path="/category/*" element={<Category />} />
82-
<Route path="/jurors/*" element={<Jurors />} />
83-
<Route path="/voting-options/*" element={<VotingOptions />} />
84-
<Route path="/notable-persons/*" element={<NotablePersons />} />
85-
<Route path="/policy/*" element={<Policy />} />
86-
<Route path="/preview/*" element={<Preview />} />
87-
</Routes>
88-
</MiddleContentContainer>
79+
<StyledEnsureAuth>
80+
<MiddleContentContainer>
81+
{isConnected && !isPreviewPage ? <Timeline /> : null}
82+
<Routes>
83+
<Route index element={<Navigate to="title" replace />} />
84+
<Route path="/title/*" element={<Title />} />
85+
<Route path="/description/*" element={<Description />} />
86+
<Route path="/court/*" element={<Court />} />
87+
<Route path="/category/*" element={<Category />} />
88+
<Route path="/jurors/*" element={<Jurors />} />
89+
<Route path="/voting-options/*" element={<VotingOptions />} />
90+
<Route path="/notable-persons/*" element={<NotablePersons />} />
91+
<Route path="/policy/*" element={<Policy />} />
92+
<Route path="/preview/*" element={<Preview />} />
93+
</Routes>
94+
</MiddleContentContainer>
95+
</StyledEnsureAuth>
8996
) : (
9097
<ConnectWalletContainer>
9198
To create a new dispute, connect first

web/src/utils/uploadFormDataToIPFS.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ import { toast } from "react-toastify";
33
import { OPTIONS } from "utils/wrapWithToast";
44

55
export function uploadFormDataToIPFS(formData: FormData, operation = "evidence"): Promise<Response> {
6+
const authToken = sessionStorage.getItem("auth-token")?.replace(/"/g, "");
7+
68
return toast.promise<Response, Error>(
79
fetch(`/.netlify/functions/uploadToIPFS?key=kleros-v2&operation=${operation}`, {
810
method: "POST",
11+
headers: {
12+
"x-auth-token": authToken ?? "",
13+
},
914
body: formData,
1015
}).then(async (response) => {
1116
if (response.status !== 200) {

0 commit comments

Comments
 (0)