Skip to content

Fixes appeal tab issues #1420

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 18, 2024
29 changes: 25 additions & 4 deletions web/src/hooks/useClassicAppealContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { useClassicAppealQuery, ClassicAppealQuery } from "queries/useClassicApp
import { useCountdown } from "hooks/useCountdown";
import { getLocalRounds } from "utils/getLocalRounds";

const LoserSideCountdownContext = createContext<number | undefined>(undefined);
interface ICountdownContext {
loserSideCountdown?: number;
winnerSideCountdown?: number;
}
const CountdownContext = createContext<ICountdownContext>({});

const OptionsContext = createContext<string[] | undefined>(undefined);

Expand Down Expand Up @@ -60,6 +64,12 @@ export const ClassicAppealProvider: React.FC<{
dispute?.court.timesPerPeriod[Periods.appeal],
multipliers?.loser_appeal_period_multiplier.toString()
);

const winnerSideCountdown = useWinnerSideCountdown(
dispute?.lastPeriodChange,
dispute?.court.timesPerPeriod[Periods.appeal]
);

const { loserRequiredFunding, winnerRequiredFunding } = useMemo(
() => ({
loserRequiredFunding: getRequiredFunding(appealCost, multipliers?.loser_stake_multiplier),
Expand All @@ -69,8 +79,11 @@ export const ClassicAppealProvider: React.FC<{
);
const fundedChoices = getFundedChoices(data?.dispute);
const [selectedOption, setSelectedOption] = useState<number | undefined>();

return (
<LoserSideCountdownContext.Provider value={loserSideCountdown}>
<CountdownContext.Provider
value={useMemo(() => ({ loserSideCountdown, winnerSideCountdown }), [loserSideCountdown, winnerSideCountdown])}
>
<SelectedOptionContext.Provider
value={useMemo(() => ({ selectedOption, setSelectedOption }), [selectedOption, setSelectedOption])}
>
Expand All @@ -89,11 +102,11 @@ export const ClassicAppealProvider: React.FC<{
<OptionsContext.Provider value={options}>{children}</OptionsContext.Provider>
</FundingContext.Provider>
</SelectedOptionContext.Provider>
</LoserSideCountdownContext.Provider>
</CountdownContext.Provider>
);
};

export const useLoserSideCountdownContext = () => useContext(LoserSideCountdownContext);
export const useCountdownContext = () => useContext(CountdownContext);
export const useSelectedOptionContext = () => useContext(SelectedOptionContext);
export const useFundingContext = () => useContext(FundingContext);
export const useOptionsContext = () => useContext(OptionsContext);
Expand Down Expand Up @@ -136,6 +149,14 @@ function useLoserSideCountdown(lastPeriodChange = "0", appealPeriodDuration = "0
return useCountdown(deadline);
}

function useWinnerSideCountdown(lastPeriodChange = "0", appealPeriodDuration = "0") {
const deadline = useMemo(
() => Number(BigInt(lastPeriodChange) + BigInt(appealPeriodDuration)),
[lastPeriodChange, appealPeriodDuration]
);
return useCountdown(deadline);
}

const getDeadline = (lastPeriodChange: string, appealPeriodDuration: string, loserTimeMultiplier: string): number => {
const parsedLastPeriodChange = BigInt(lastPeriodChange);
const parsedAppealPeriodDuration = BigInt(appealPeriodDuration);
Expand Down
81 changes: 43 additions & 38 deletions web/src/pages/Cases/CaseDetails/Appeal/Classic/Fund.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import styled from "styled-components";
import { useParams } from "react-router-dom";
import { useAccount, useBalance, usePublicClient } from "wagmi";
Expand All @@ -9,15 +9,12 @@ import { isUndefined } from "utils/index";
import { EnsureChain } from "components/EnsureChain";
import { usePrepareDisputeKitClassicFundAppeal, useDisputeKitClassicFundAppeal } from "hooks/contracts/generated";
import { useParsedAmount } from "hooks/useParsedAmount";
import {
useLoserSideCountdownContext,
useSelectedOptionContext,
useFundingContext,
} from "hooks/useClassicAppealContext";
import { useSelectedOptionContext, useFundingContext, useCountdownContext } from "hooks/useClassicAppealContext";

const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
`;

Expand All @@ -38,11 +35,14 @@ const StyledField = styled(Field)`

const StyledButton = styled(Button)`
margin: auto;
margin-top: 12px;
margin-top: 4px;
`;

const StyledLabel = styled.label`
align-self: flex-start;
`;
const useNeedFund = () => {
const loserSideCountdown = useLoserSideCountdownContext();
const { loserSideCountdown } = useCountdownContext();
const { fundedChoices, winningChoice } = useFundingContext();
const needFund =
(loserSideCountdown ?? 0) > 0 ||
Expand All @@ -57,15 +57,15 @@ const useNeedFund = () => {
const useFundAppeal = (parsedAmount) => {
const { id } = useParams();
const { selectedOption } = useSelectedOptionContext();
const { config: fundAppealConfig } = usePrepareDisputeKitClassicFundAppeal({
const { config: fundAppealConfig, isError } = usePrepareDisputeKitClassicFundAppeal({
enabled: !isUndefined(id) && !isUndefined(selectedOption),
args: [BigInt(id ?? 0), BigInt(selectedOption ?? 0)],
value: parsedAmount,
});

const { writeAsync: fundAppeal } = useDisputeKitClassicFundAppeal(fundAppealConfig);

return fundAppeal;
return { fundAppeal, isError };
};

interface IFund {
Expand All @@ -89,39 +89,44 @@ const Fund: React.FC<IFund> = ({ amount, setAmount, setIsOpen }) => {
const parsedAmount = useParsedAmount(debouncedAmount);

const [isSending, setIsSending] = useState(false);
const fundAppeal = useFundAppeal(parsedAmount);
const { fundAppeal, isError } = useFundAppeal(parsedAmount);

const isFundDisabled = useMemo(
() =>
isDisconnected || isSending || !balance || parsedAmount > balance.value || Number(parsedAmount) <= 0 || isError,
[isDisconnected, isSending, balance, parsedAmount, isError]
);

return needFund ? (
<Container>
<label>How much ETH do you want to contribute?</label>
<div>
<StyledField
type="number"
value={amount}
onChange={(e) => {
setAmount(e.target.value);
<StyledLabel>How much ETH do you want to contribute?</StyledLabel>
<StyledField
type="number"
value={amount}
onChange={(e) => {
setAmount(e.target.value);
}}
placeholder="Amount to fund"
/>
<EnsureChain>
<StyledButton
disabled={isFundDisabled}
isLoading={isSending}
text={isDisconnected ? "Connect to Fund" : "Fund"}
onClick={() => {
if (fundAppeal) {
setIsSending(true);
wrapWithToast(async () => await fundAppeal().then((response) => response.hash), publicClient)
.then((res) => {
res.status && setIsOpen(true);
})
.finally(() => {
setIsSending(false);
});
}
}}
placeholder="Amount to fund"
/>
<EnsureChain>
<StyledButton
disabled={isDisconnected || isSending || !balance || parsedAmount > balance.value}
text={isDisconnected ? "Connect to Fund" : "Fund"}
onClick={() => {
if (fundAppeal) {
setIsSending(true);
wrapWithToast(async () => await fundAppeal().then((response) => response.hash), publicClient)
.then(() => {
setIsOpen(true);
})
.finally(() => {
setIsSending(false);
});
}
}}
/>
</EnsureChain>
</div>
</EnsureChain>
</Container>
) : (
<></>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import styled from "styled-components";
import StageExplainer from "../StageExplainer";
import OptionCard from "../../OptionCard";
import {
useCountdownContext,
useFundingContext,
useLoserSideCountdownContext,
useOptionsContext,
useSelectedOptionContext,
} from "hooks/useClassicAppealContext";
Expand All @@ -27,14 +27,14 @@ interface IStageOne {
}

const StageOne: React.FC<IStageOne> = ({ setAmount }) => {
const { paidFees, winningChoice, loserRequiredFunding, winnerRequiredFunding } = useFundingContext();
const { paidFees, winningChoice, loserRequiredFunding, winnerRequiredFunding, fundedChoices } = useFundingContext();
const options = useOptionsContext();
const loserSideCountdown = useLoserSideCountdownContext();
const { loserSideCountdown } = useCountdownContext();
const { selectedOption, setSelectedOption } = useSelectedOptionContext();

return (
<Container>
<StageExplainer {...{ loserSideCountdown }} />
<StageExplainer countdown={loserSideCountdown} stage={1} />
<label> Which option do you want to fund? </label>
<OptionsContainer>
{!isUndefined(paidFees) &&
Expand All @@ -50,6 +50,7 @@ const StageOne: React.FC<IStageOne> = ({ setAmount }) => {
winner={i.toString() === winningChoice}
funding={paidFees[i] ? BigInt(paidFees[i]) : 0n}
required={requiredFunding}
canBeSelected={!fundedChoices?.includes(i.toString())}
onClick={() => {
setSelectedOption(i);
setAmount(formatUnitsWei(requiredFunding));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import React, { useEffect } from "react";
import styled from "styled-components";
import OptionCard from "../../OptionCard";
import { useFundingContext, useOptionsContext, useSelectedOptionContext } from "hooks/useClassicAppealContext";
import {
useCountdownContext,
useFundingContext,
useOptionsContext,
useSelectedOptionContext,
} from "hooks/useClassicAppealContext";
import { isUndefined } from "utils/index";
import { formatUnitsWei } from "utils/format";
import StageExplainer from "../StageExplainer";
import Skeleton from "react-loading-skeleton";

const Container = styled.div`
margin: 24px 0;
Expand All @@ -22,35 +29,39 @@ interface IStageTwo {

const StageTwo: React.FC<IStageTwo> = ({ setAmount }) => {
const { paidFees, winningChoice, winnerRequiredFunding, fundedChoices } = useFundingContext();
const { winnerSideCountdown } = useCountdownContext();
const options = useOptionsContext();
const { selectedOption, setSelectedOption } = useSelectedOptionContext();
useEffect(() => {
if (!isUndefined(winningChoice)) setSelectedOption(parseInt(winningChoice));
if (!isUndefined(winnerRequiredFunding)) setAmount(formatUnitsWei(winnerRequiredFunding));
});
}, [winnerRequiredFunding, winningChoice]);

return (
<Container>
{!isUndefined(winningChoice) &&
!isUndefined(fundedChoices) &&
!isUndefined(paidFees) &&
fundedChoices.length > 0 &&
!fundedChoices.includes(winningChoice) ? (
{!isUndefined(winningChoice) && !isUndefined(fundedChoices) && !isUndefined(paidFees) ? (
<>
<label>Loser deadline has finalized, you can only fund the current winner.</label>
<OptionsContainer>
<OptionCard
text={options![winningChoice!]}
selected={winningChoice === selectedOption}
winner={true}
funding={paidFees![winningChoice!] ? BigInt(paidFees![winningChoice!]) : 0n}
required={winnerRequiredFunding!}
canBeSelected={false}
onClick={() => setSelectedOption(parseInt(winningChoice!, 10))}
/>
</OptionsContainer>
{fundedChoices.length > 0 && !fundedChoices.includes(winningChoice) ? (
<>
<StageExplainer stage={2} countdown={winnerSideCountdown} />
<OptionsContainer>
<OptionCard
text={options![winningChoice!]}
selected={parseInt(winningChoice) === selectedOption}
winner={true}
funding={paidFees![winningChoice!] ? BigInt(paidFees![winningChoice!]) : 0n}
required={winnerRequiredFunding!}
canBeSelected={false}
onClick={() => setSelectedOption(parseInt(winningChoice!, 10))}
/>
</OptionsContainer>
</>
) : (
<label>No losing option has been funded in time, winner is maintained.</label>
)}
</>
) : (
<label>No losing option has been funded in time, winner is maintained.</label>
<Skeleton height={140} />
)}
</Container>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import styled from "styled-components";
import { useLoserSideCountdownContext } from "hooks/useClassicAppealContext";
import { useCountdownContext } from "hooks/useClassicAppealContext";
import { StyledSkeleton } from "components/StyledSkeleton";
import StageOne from "./StageOne";
import StageTwo from "./StageTwo";
Expand All @@ -15,7 +15,7 @@ interface IOptions {
}

const Options: React.FC<IOptions> = ({ setAmount }) => {
const loserSideCountdown = useLoserSideCountdownContext();
const { loserSideCountdown } = useCountdownContext();
return !isUndefined(loserSideCountdown) ? (
<Container>
{loserSideCountdown > 0 ? <StageOne setAmount={setAmount} /> : <StageTwo setAmount={setAmount} />}
Expand Down
Loading