Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
33 changes: 29 additions & 4 deletions src/components/disputeDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -373,6 +379,21 @@ class DisputeDetails extends React.Component {
</Card.Body>
);

renderEscrowV1AppealSection = (disputePeriod, appealCallback, appealCost) => (
<Card.Body>
<div className="h1">{disputePeriod == DISPUTE_PERIOD_APPEAL ? "Appeal the decision" : "Appeal period ended"}</div>
{disputePeriod == DISPUTE_PERIOD_APPEAL && <p className="label">In order to appeal the decision, you need to pay the appeal cost.</p>}
{disputePeriod == DISPUTE_PERIOD_APPEAL && (
<Button
onClick={() =>
appealCallback(0, ethers.formatEther(appealCost))
}>
Appeal - {ethers.formatEther(appealCost)} ETH
</Button>
)}
</Card.Body>
);

// Helper method to render question section
renderQuestionSection = (metaevidenceJSON, arbitratorDisputeID) => (
<Card.Body className={styles.question}>
Expand Down Expand Up @@ -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;
}
Expand All @@ -455,7 +476,9 @@ class DisputeDetails extends React.Component {
Appeal
</Accordion.Toggle>
<Accordion.Collapse eventKey="1">
{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)}
</Accordion.Collapse>
</Card>
);
Expand Down Expand Up @@ -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(
<Col key={`fallback-${index + 1}`} className="pb-4" xl={8} lg={12} xs={24}>
Expand Down Expand Up @@ -672,6 +695,7 @@ class DisputeDetails extends React.Component {
render() {
const {
arbitrated,
network,
metaevidenceJSON,
evidences,
arbitratorDisputeID,
Expand Down Expand Up @@ -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 (
<section className={styles.disputeDetails}>
Expand All @@ -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)}

<Card>
<Accordion.Toggle className={activeKey == 2 ? "open" : "closed"} as={Card.Header} eventKey="2">
Expand Down
25 changes: 13 additions & 12 deletions src/containers/interact.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
})
]);

Expand Down Expand Up @@ -412,20 +412,20 @@ class Interact extends React.Component {
renderIncompatibleWarning = () => {
const { metaevidence } = this.state;
const isGenericMetaEvidence = metaevidence?.metaEvidenceJSON?.category === "Non-Standard Contract";

return (
<div style={{
padding: "1rem 2rem",
fontSize: "14px",
<div style={{
padding: "1rem 2rem",
fontSize: "14px",
background: isGenericMetaEvidence ? "#fff3cd" : "#fafafa",
border: isGenericMetaEvidence ? "1px solid #ffeaa7" : "none",
borderRadius: "4px",
marginBottom: "1rem"
}}>
{isGenericMetaEvidence ? (
<>
<b>⚠️ Non-Standard Contract:</b> 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
<b>⚠️ Non-Standard Contract:</b> 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.
<br />
<small style={{ color: "#856404", marginTop: "0.5rem", display: "block" }}>
Expand All @@ -434,7 +434,7 @@ class Interact extends React.Component {
</>
) : (
<>
<b>View mode only:</b> the arbitrable contract of this dispute is not compatible with the interface of Dispute Resolver.
<b>View mode only:</b> 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.
</>
)}
Expand Down Expand Up @@ -558,6 +558,7 @@ class Interact extends React.Component {
/>
<DisputeDetails
activeAddress={activeAddress}
network={network}
metaevidenceJSON={metaevidence?.metaEvidenceJSON}
evidences={evidences}
ipfsGateway="https://cdn.kleros.link"
Expand Down