diff --git a/src/app.js b/src/app.js index 9fdec4f..bb3b864 100644 --- a/src/app.js +++ b/src/app.js @@ -1812,6 +1812,13 @@ class App extends React.Component { }; appeal = async (arbitrableAddress, arbitrableDisputeID, party, contribution) => { + //EscrowV1 does not support crowdfunding and so does not have the fundAppeal function, users can only appeal by paying the appeal cost + if (networkMap[this.state.network].ESCROW_V1_CONTRACTS + .includes(arbitrableAddress)) { + + return this.handleEscrowV1Appeal(arbitrableAddress, arbitrableDisputeID, contribution); + } + const contract = await getSignableContract( "IDisputeResolver", arbitrableAddress, @@ -1830,6 +1837,25 @@ class App extends React.Component { } } + handleEscrowV1Appeal = async (arbitrableAddress, arbitrableDisputeID, contribution) => { + const contract = await getSignableContract( + "MultipleArbitrableTokenTransaction", + arbitrableAddress, + this.state.provider + ); + + try { + const tx = await contract.appeal( + arbitrableDisputeID, + { value: ethers.parseEther(contribution) } + ); + return tx.wait(); + } catch (err) { + console.error("EscrowV1 appeal failed:", err); + return null; + } + } + withdrawFeesAndRewardsForAllRounds = async ( arbitrableAddress, arbitrableDisputeID, diff --git a/src/components/disputeDetails.js b/src/components/disputeDetails.js index c857c8f..4364125 100644 --- a/src/components/disputeDetails.js +++ b/src/components/disputeDetails.js @@ -9,6 +9,7 @@ import DisputeTimeline from "components/disputeTimeline"; import EvidenceTimeline from "components/evidenceTimeline"; import CrowdfundingCard from "components/crowdfundingCard"; import { ethers } from "ethers"; +import networkMap from "../ethereum/network-contract-mapping"; import AlertMessage from "components/alertMessage"; @@ -66,6 +67,11 @@ class DisputeDetails extends React.Component { const { currentRuling, appealCost, multipliers, exceptionalContractAddresses, arbitrated } = this.props; + //Means it is an EscrowV1 dispute, for which there are no calculations needed + if (networkMap[this.props.network].ESCROW_V1_CONTRACTS.includes(arbitrated)) { + return appealCost; + } + let stake; if (currentRuling == rulingOption || (exceptionalContractAddresses.includes(arbitrated) && currentRuling == 0)) { @@ -373,6 +379,21 @@ class DisputeDetails extends React.Component { ); + renderEscrowV1AppealSection = (disputePeriod, appealCallback, appealCost) => ( + +
{disputePeriod == DISPUTE_PERIOD_APPEAL ? "Appeal the decision" : "Appeal period ended"}
+ {disputePeriod == DISPUTE_PERIOD_APPEAL &&

In order to appeal the decision, you need to pay the appeal cost.

} + {disputePeriod == DISPUTE_PERIOD_APPEAL && ( + + )} +
+ ); + // Helper method to render question section renderQuestionSection = (metaevidenceJSON, arbitratorDisputeID) => ( @@ -444,7 +465,7 @@ class DisputeDetails extends React.Component { }; // Helper method to render appeal card conditionally - renderAppealCard = (arbitratorDispute, disputePeriod, contributions, multipliers, appealCost, appealPeriod, arbitrated, totalWithdrawable, metaevidenceJSON, currentRuling, appealCallback, exceptionalContractAddresses, activeKey) => { + renderAppealCard = (arbitratorDispute, disputePeriod, contributions, multipliers, appealCost, appealPeriod, arbitrated, totalWithdrawable, metaevidenceJSON, currentRuling, appealCallback, exceptionalContractAddresses, activeKey, isEscrowV1Dispute) => { if (!arbitratorDispute || disputePeriod < DISPUTE_PERIOD_APPEAL || !contributions || !multipliers || !appealCost || !appealPeriod || !arbitrated) { return null; } @@ -455,7 +476,9 @@ class DisputeDetails extends React.Component { Appeal - {this.renderAppealSection(disputePeriod, totalWithdrawable, metaevidenceJSON, currentRuling, contributions, appealCallback, exceptionalContractAddresses, arbitrated)} + {isEscrowV1Dispute ? + this.renderEscrowV1AppealSection(disputePeriod, appealCallback, appealCost) + : this.renderAppealSection(disputePeriod, totalWithdrawable, metaevidenceJSON, currentRuling, contributions, appealCallback, exceptionalContractAddresses, arbitrated)} ); @@ -557,7 +580,7 @@ class DisputeDetails extends React.Component { // Try to use Reality.eth parsed outcomes if available const realityOutcomes = this.findRealityOutcomes(metaevidenceJSON); const fallbackTitles = realityOutcomes || ["Yes", "No"]; // Basic fallback for Reality.eth questions - + fallbackTitles.forEach((title, index) => { cards.push( @@ -672,6 +695,7 @@ class DisputeDetails extends React.Component { render() { const { arbitrated, + network, metaevidenceJSON, evidences, arbitratorDisputeID, @@ -702,6 +726,7 @@ class DisputeDetails extends React.Component { } const disputePeriod = parseInt(arbitratorDispute.period, 10); + const isEscrowV1Dispute = networkMap[network].ESCROW_V1_CONTRACTS.includes(arbitrated); return (
@@ -720,7 +745,7 @@ class DisputeDetails extends React.Component { className={`mt-4 ${styles.accordion}`} onSelect={this.handleAccordionSelect} > - {this.renderAppealCard(arbitratorDispute, disputePeriod, contributions, multipliers, appealCost, appealPeriod, arbitrated, totalWithdrawable, metaevidenceJSON, currentRuling, appealCallback, exceptionalContractAddresses, activeKey)} + {this.renderAppealCard(arbitratorDispute, disputePeriod, contributions, multipliers, appealCost, appealPeriod, arbitrated, totalWithdrawable, metaevidenceJSON, currentRuling, appealCallback, exceptionalContractAddresses, activeKey, isEscrowV1Dispute)} diff --git a/src/containers/interact.js b/src/containers/interact.js index ba4a8fe..6860c2e 100644 --- a/src/containers/interact.js +++ b/src/containers/interact.js @@ -210,11 +210,11 @@ class Interact extends React.Component { this.props.getMultipliersCallback(arbitrated).catch(err => { console.warn('getMultipliersCallback failed:', err.message); return { - winner: "10000", // 1.0 multiplier in basis points - loser: "10000", - shared: "5000", - divisor: "10000" - }; + winnerStakeMultiplier: 10000n, + loserStakeMultiplier: 10000n, + loserAppealPeriodMultiplier: 10000n, + denominator: 10000n + } }) ]); @@ -412,11 +412,11 @@ class Interact extends React.Component { renderIncompatibleWarning = () => { const { metaevidence } = this.state; const isGenericMetaEvidence = metaevidence?.metaEvidenceJSON?.category === "Non-Standard Contract"; - + return ( -
{isGenericMetaEvidence ? ( <> - ⚠️ Non-Standard Contract: This arbitrable contract doesn't follow standard Kleros patterns. - The dispute information shown is generic. Limited functionality is available - you may not be able to submit evidence + ⚠️ Non-Standard Contract: This arbitrable contract doesn't follow standard Kleros patterns. + The dispute information shown is generic. Limited functionality is available - you may not be able to submit evidence or fund appeals through this interface.
@@ -434,7 +434,7 @@ class Interact extends React.Component { ) : ( <> - View mode only: the arbitrable contract of this dispute is not compatible with the interface of Dispute Resolver. + View mode only: the arbitrable contract of this dispute is not compatible with the interface of Dispute Resolver. You can't submit evidence or fund appeal on this interface. You can do these on the arbitrable application, if implemented. )} @@ -558,6 +558,7 @@ class Interact extends React.Component { />