Skip to content

Commit ca27499

Browse files
committed
fix: reimbursement and fee withdrawal
1 parent abae57c commit ca27499

File tree

3 files changed

+145
-83
lines changed

3 files changed

+145
-83
lines changed

.vscode/launch.json

-19
This file was deleted.

contracts/GeneralizedTCR.sol

+47-15
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,11 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
6565
}
6666

6767
struct Round {
68-
uint[3] paidFees; // Tracks the fees paid for each Party in this round.
68+
uint[3] amountPaid; // Tracks the sum for each Party in this round. Includes arbitration fees, fee stakes and deposits.
6969
bool[3] hasPaid; // True if the Party has fully paid its fee in this round.
7070
uint feeRewards; // Sum of reimbursable fees and stake rewards available to the parties that made contributions to the side that ultimately wins a dispute.
7171
mapping(address => uint[3]) contributions; // Maps contributors to their contributions for each side.
72+
uint[3] paidArbitrationFees; // The arbitration or appeal fees paid for each party.
7273
}
7374

7475
struct RequestID {
@@ -297,8 +298,9 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
297298
: removalChallengeBaseDeposit;
298299
uint totalCost = arbitrationCost.addCap(challengerBaseDeposit);
299300
contribute(round, Party.Challenger, msg.sender, msg.value, totalCost);
300-
require(round.paidFees[uint(Party.Challenger)] >= totalCost, "You must fully fund your side.");
301+
require(round.amountPaid[uint(Party.Challenger)] >= totalCost, "You must fully fund your side.");
301302
round.hasPaid[uint(Party.Challenger)] = true;
303+
round.paidArbitrationFees[uint(Party.Challenger)] = arbitrationCost;
302304

303305
// Raise a dispute.
304306
request.disputeID = request.arbitrator.createDispute.value(arbitrationCost)(RULING_OPTIONS, request.arbitratorExtraData);
@@ -364,6 +366,7 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
364366
uint appealCost = request.arbitrator.appealCost(request.disputeID, request.arbitratorExtraData);
365367
uint totalCost = appealCost.addCap((appealCost.mulCap(multiplier)) / MULTIPLIER_DIVISOR);
366368
uint contribution = contribute(round, _side, msg.sender, msg.value, totalCost);
369+
round.paidArbitrationFees[uint(_side)] += appealCost;
367370

368371
emit AppealContribution(
369372
_itemID,
@@ -374,7 +377,7 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
374377
_side
375378
);
376379

377-
if (round.paidFees[uint(_side)] >= totalCost) {
380+
if (round.amountPaid[uint(_side)] >= totalCost) {
378381
round.hasPaid[uint(_side)] = true;
379382
emit HasPaidAppealFee(_itemID, items[_itemID].requests.length - 1, request.rounds.length - 1, _side);
380383
}
@@ -403,20 +406,48 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
403406
if (!round.hasPaid[uint(Party.Requester)] || !round.hasPaid[uint(Party.Challenger)]) {
404407
// Reimburse if not enough fees were raised to appeal the ruling.
405408
reward = round.contributions[_beneficiary][uint(Party.Requester)] + round.contributions[_beneficiary][uint(Party.Challenger)];
409+
} else if (
410+
request.ruling == Party.None &&
411+
_round == 0
412+
) {
413+
if (
414+
_beneficiary != request.parties[uint(Party.Requester)] &&
415+
_beneficiary != request.parties[uint(Party.Challenger)]
416+
) return;
417+
418+
uint side = _beneficiary == request.parties[uint(Party.Requester)] ? 1 : 2;
419+
if (round.contributions[_beneficiary][side] == 0) return;
420+
421+
// The first round is special since it does not include crowdfunding fees,
422+
// or stake multipliers. The only participants are the requester and challenger.
423+
//
424+
// Arbitration costs can rise or fall between when a request was made and when it was
425+
// challenged. To keep things fair, we reimburse proportionally to the amount
426+
// contributed.
427+
reward = round.contributions[_beneficiary][side].subCap(round.paidArbitrationFees[side]); // Base deposit.
428+
uint arbitrationFeesSide = round.paidArbitrationFees[side];
429+
uint totalArbitrationFeesPaid = round.paidArbitrationFees[uint(Party.Requester)] + round.paidArbitrationFees[uint(Party.Requester)];
430+
431+
// Reimbursable fees are total amount paid minus the amount paid by the challenger
432+
// because the amount paid by the challenger is the actual cost of the round.
433+
uint reimbursableFeesAvailable = totalArbitrationFeesPaid - round.paidArbitrationFees[uint(Party.Challenger)];
434+
435+
reward += arbitrationFeesSide * reimbursableFeesAvailable / totalArbitrationFeesPaid;
436+
406437
} else if (request.ruling == Party.None) {
407438
// Reimburse unspent fees proportionally if there is no winner or loser.
408-
uint rewardRequester = round.paidFees[uint(Party.Requester)] > 0
409-
? (round.contributions[_beneficiary][uint(Party.Requester)] * round.feeRewards) / (round.paidFees[uint(Party.Challenger)] + round.paidFees[uint(Party.Requester)])
439+
uint rewardRequester = round.amountPaid[uint(Party.Requester)] > 0
440+
? (round.contributions[_beneficiary][uint(Party.Requester)] * round.feeRewards) / (round.amountPaid[uint(Party.Challenger)] + round.amountPaid[uint(Party.Requester)])
410441
: 0;
411-
uint rewardChallenger = round.paidFees[uint(Party.Challenger)] > 0
412-
? (round.contributions[_beneficiary][uint(Party.Challenger)] * round.feeRewards) / (round.paidFees[uint(Party.Challenger)] + round.paidFees[uint(Party.Requester)])
442+
uint rewardChallenger = round.amountPaid[uint(Party.Challenger)] > 0
443+
? (round.contributions[_beneficiary][uint(Party.Challenger)] * round.feeRewards) / (round.amountPaid[uint(Party.Challenger)] + round.amountPaid[uint(Party.Requester)])
413444
: 0;
414445

415446
reward = rewardRequester + rewardChallenger;
416447
} else {
417448
// Reward the winner.
418-
reward = round.paidFees[uint(request.ruling)] > 0
419-
? (round.contributions[_beneficiary][uint(request.ruling)] * round.feeRewards) / round.paidFees[uint(request.ruling)]
449+
reward = round.amountPaid[uint(request.ruling)] > 0
450+
? (round.contributions[_beneficiary][uint(request.ruling)] * round.feeRewards) / round.amountPaid[uint(request.ruling)]
420451
: 0;
421452

422453
}
@@ -624,8 +655,9 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
624655
uint arbitrationCost = request.arbitrator.arbitrationCost(request.arbitratorExtraData);
625656
uint totalCost = arbitrationCost.addCap(_baseDeposit);
626657
contribute(round, Party.Requester, msg.sender, msg.value, totalCost);
627-
require(round.paidFees[uint(Party.Requester)] >= totalCost, "You must fully fund your side.");
658+
require(round.amountPaid[uint(Party.Requester)] >= totalCost, "You must fully fund your side.");
628659
round.hasPaid[uint(Party.Requester)] = true;
660+
round.paidArbitrationFees[uint(Party.Requester)] = arbitrationCost;
629661

630662
emit ItemStatusChange(itemID, item.requests.length - 1, request.rounds.length - 1, false, false);
631663
emit RequestSubmitted(itemID, item.requests.length - 1, item.status);
@@ -660,9 +692,9 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
660692
// Take up to the amount necessary to fund the current round at the current costs.
661693
uint contribution; // Amount contributed.
662694
uint remainingETH; // Remaining ETH to send back.
663-
(contribution, remainingETH) = calculateContribution(_amount, _totalRequired.subCap(_round.paidFees[uint(_side)]));
695+
(contribution, remainingETH) = calculateContribution(_amount, _totalRequired.subCap(_round.amountPaid[uint(_side)]));
664696
_round.contributions[_contributor][uint(_side)] += contribution;
665-
_round.paidFees[uint(_side)] += contribution;
697+
_round.amountPaid[uint(_side)] += contribution;
666698
_round.feeRewards += contribution;
667699

668700
// Reimburse leftover ETH.
@@ -699,7 +731,7 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
699731

700732
emit ItemStatusChange(itemID, item.requests.length - 1, request.rounds.length - 1, true, true);
701733

702-
// Automatically withdraw.
734+
// Automatically withdraw first deposits and reimbursements (first round only).
703735
if (winner == Party.None) {
704736
withdrawFeesAndRewards(request.parties[uint(Party.Requester)], itemID, item.requests.length - 1, 0);
705737
withdrawFeesAndRewards(request.parties[uint(Party.Challenger)], itemID, item.requests.length - 1, 0);
@@ -808,7 +840,7 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
808840
view
809841
returns (
810842
bool appealed,
811-
uint[3] memory paidFees,
843+
uint[3] memory amountPaid,
812844
bool[3] memory hasPaid,
813845
uint feeRewards
814846
)
@@ -818,7 +850,7 @@ contract GeneralizedTCR is IArbitrable, IEvidence {
818850
Round storage round = request.rounds[_round];
819851
return (
820852
_round != (request.rounds.length - 1),
821-
round.paidFees,
853+
round.amountPaid,
822854
round.hasPaid,
823855
round.feeRewards
824856
);

0 commit comments

Comments
 (0)