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
8 changes: 6 additions & 2 deletions contracts/src/arbitration/arbitrables/ArbitrableExample.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ contract ArbitrableExample is IArbitrableV2 {
mapping(uint256 => uint256) public externalIDtoLocalID; // Maps external (arbitrator side) dispute IDs to local dispute IDs.
DisputeStruct[] public disputes; // Stores the disputes' info. disputes[disputeID].

uint256 public numberOfRulingOptions = 2;

// ************************************* //
// * Function Modifiers * //
// ************************************* //
Expand Down Expand Up @@ -100,6 +102,10 @@ contract ArbitrableExample is IArbitrableV2 {
templateId = templateRegistry.setDisputeTemplate("", _templateData, _templateDataMappings);
}

function changeNumberOfRulingOptions(uint256 _numberOfRulingOptions) external onlyByOwner {
numberOfRulingOptions = _numberOfRulingOptions;
}

// ************************************* //
// * State Modifiers * //
// ************************************* //
Expand All @@ -111,7 +117,6 @@ contract ArbitrableExample is IArbitrableV2 {
function createDispute(string calldata _action) external payable returns (uint256 disputeID) {
emit Action(_action);

uint256 numberOfRulingOptions = 2;
uint256 localDisputeID = disputes.length;
disputes.push(DisputeStruct({isRuled: false, ruling: 0, numberOfRulingOptions: numberOfRulingOptions}));

Expand All @@ -130,7 +135,6 @@ contract ArbitrableExample is IArbitrableV2 {
function createDispute(string calldata _action, uint256 _feeInWeth) external returns (uint256 disputeID) {
emit Action(_action);

uint256 numberOfRulingOptions = 2;
uint256 localDisputeID = disputes.length;
disputes.push(DisputeStruct({isRuled: false, ruling: 0, numberOfRulingOptions: numberOfRulingOptions}));

Expand Down
126 changes: 126 additions & 0 deletions contracts/test/foundry/KlerosCore_Appeals.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -500,4 +500,130 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase {
emit KlerosCore.NewPeriod(disputeID, KlerosCore.Period.execution);
core.passPeriod(disputeID);
}

function testFuzz_appeal(uint256 numberOfOptions, uint256 choice1, uint256 choice2, uint256 choice3) public {
uint256 disputeID = 0;

arbitrable.changeNumberOfRulingOptions(numberOfOptions);

// Have only 2 options for 3 jurors to create a majority
vm.assume(choice1 <= numberOfOptions);
vm.assume(choice2 <= numberOfOptions);
vm.assume(choice3 <= numberOfOptions); // Will be used for appeal

vm.prank(staker1);
core.setStake(GENERAL_COURT, 2000);
vm.prank(disputer);
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");

(uint256 numberOfChoices, , ) = disputeKit.disputes(disputeID);

assertEq(numberOfChoices, numberOfOptions, "Wrong numberOfChoices");

vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase

// Split the stakers' votes. The first staker will get VoteID 0 and the second will take the rest.
core.draw(disputeID, 1);

vm.warp(block.timestamp + maxDrawingTime);
sortitionModule.passPhase(); // Staking phase to stake the 2nd voter
vm.prank(staker2);
core.setStake(GENERAL_COURT, 20000);
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase

core.draw(disputeID, 2); // Assign leftover votes to staker2

vm.warp(block.timestamp + timesPerPeriod[0]);
core.passPeriod(disputeID); // Vote

uint256[] memory voteIDs = new uint256[](1);
voteIDs[0] = 0;
vm.prank(staker1);
disputeKit.castVote(disputeID, voteIDs, choice1, 0, "XYZ"); // Staker1 only got 1 vote because of low stake

voteIDs = new uint256[](2);
voteIDs[0] = 1;
voteIDs[1] = 2;
vm.prank(staker2);
disputeKit.castVote(disputeID, voteIDs, choice2, 0, "XYZ");
core.passPeriod(disputeID); // Appeal

vm.assume(choice3 != choice2);
vm.prank(crowdfunder1);
disputeKit.fundAppeal{value: 0.63 ether}(disputeID, choice3); // Fund the losing choice. Total cost will be 0.63 (0.21 + 0.21 * (20000/10000))

assertEq((disputeKit.getFundedChoices(disputeID)).length, 1, "1 choice should be funded");

vm.prank(crowdfunder1);
disputeKit.fundAppeal{value: 0.42 ether}(disputeID, choice2); // Fund the winning choice. Total cost will be 0.42 (0.21 + 0.21 * (10000/10000))

assertEq((disputeKit.getFundedChoices(disputeID)).length, 0, "No funded choices in a fresh round");
}

function testFuzz_fundAppeal_msgValue(uint256 appealValue) public {
uint256 disputeID = 0;

vm.assume(appealValue <= 10 ether);
vm.deal(crowdfunder1, 10 ether);

vm.prank(staker1);
core.setStake(GENERAL_COURT, 2000);
vm.prank(disputer);
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");

vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase

// Split the stakers' votes. The first staker will get VoteID 0 and the second will take the rest.
core.draw(disputeID, 1);

vm.warp(block.timestamp + maxDrawingTime);
sortitionModule.passPhase(); // Staking phase to stake the 2nd voter
vm.prank(staker2);
core.setStake(GENERAL_COURT, 20000);
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase

core.draw(disputeID, 2); // Assign leftover votes to staker2

vm.warp(block.timestamp + timesPerPeriod[0]);
core.passPeriod(disputeID); // Vote

uint256[] memory voteIDs = new uint256[](1);
voteIDs[0] = 0;
vm.prank(staker1);
disputeKit.castVote(disputeID, voteIDs, 1, 0, "XYZ"); // Staker1 only got 1 vote because of low stake

voteIDs = new uint256[](2);
voteIDs[0] = 1;
voteIDs[1] = 2;
vm.prank(staker2);
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
core.passPeriod(disputeID); // Appeal

vm.prank(crowdfunder1);
disputeKit.fundAppeal{value: appealValue}(disputeID, 1); // Fund the losing choice

if (appealValue >= 0.63 ether) {
// 0.63 eth is the required amount for losing side.
assertEq((disputeKit.getFundedChoices(disputeID)).length, 1, "One choice should be funded");
// Dispute kit shouldn't demand more value than necessary
assertEq(crowdfunder1.balance, 9.37 ether, "Wrong balance of the crowdfunder");
assertEq(address(disputeKit).balance, 0.63 ether, "Wrong balance of the DK");
} else {
assertEq((disputeKit.getFundedChoices(disputeID)).length, 0, "No choices should be funded");
assertEq(crowdfunder1.balance, 10 ether - appealValue, "Wrong balance of the crowdfunder");
assertEq(address(disputeKit).balance, appealValue, "Wrong balance of the DK");
}
}
}
47 changes: 46 additions & 1 deletion contracts/test/foundry/KlerosCore_Disputes.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ contract KlerosCore_DisputesTest is KlerosCore_TestBase {
assertEq(jumped, false, "jumped should be false");
assertEq(extraData, newExtraData, "Wrong extra data");
assertEq(disputeKit.coreDisputeIDToLocal(0), disputeID, "Wrong local disputeID");
assertEq(disputeKit.coreDisputeIDToActive(0), true, "Wrong disputes length");
assertEq(disputeKit.coreDisputeIDToActive(0), true, "Dispute should be active in this DK");

(
uint256 winningChoice,
Expand Down Expand Up @@ -146,4 +146,49 @@ contract KlerosCore_DisputesTest is KlerosCore_TestBase {
assertEq(feeToken.balanceOf(address(core)), 0.18 ether, "Wrong token balance of the core");
assertEq(feeToken.balanceOf(disputer), 0.82 ether, "Wrong token balance of the disputer");
}

function testFuzz_createDispute_msgValue(uint256 disputeValue) public {
uint256 disputeID = 0;
uint256 arbitrationCost = core.arbitrationCost(arbitratorExtraData);

// Cap it to 10 eth, so the number of jurors is not astronomical.
vm.assume(disputeValue >= arbitrationCost && disputeValue <= 10 ether);
vm.deal(disputer, 10 ether);

vm.prank(staker1);
core.setStake(GENERAL_COURT, 2000);
vm.prank(disputer);
arbitrable.createDispute{value: disputeValue}("Action");

KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
assertEq(round.totalFeesForJurors, disputeValue, "Wrong totalFeesForJurors");
assertEq(round.nbVotes, disputeValue / feeForJuror, "Wrong nbVotes");

vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase

core.draw(disputeID, disputeValue / feeForJuror);

vm.warp(block.timestamp + timesPerPeriod[0]);
core.passPeriod(disputeID); // Vote

uint256[] memory voteIDs = new uint256[](disputeValue / feeForJuror);
for (uint256 i = 0; i < voteIDs.length; i++) {
voteIDs[i] = i;
}

vm.prank(staker1);
disputeKit.castVote(disputeID, voteIDs, 1, 0, "XYZ");

core.passPeriod(disputeID); // Appeal

vm.warp(block.timestamp + timesPerPeriod[3]);
core.passPeriod(disputeID); // Execution

vm.expectEmit(true, true, true, true);
emit IArbitrableV2.Ruling(IArbitratorV2(address(core)), disputeID, 1);
core.executeRuling(disputeID);
}
}
86 changes: 86 additions & 0 deletions contracts/test/foundry/KlerosCore_Drawing.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,90 @@ contract KlerosCore_DrawingTest is KlerosCore_TestBase {
(, , , , uint256 nbVoters, ) = disputeKit.getRoundInfo(disputeID, roundID, 0);
assertEq(nbVoters, 3, "nbVoters should be 3");
}

function testFuzz_drawIterations(uint256 iterations) public {
uint256 disputeID = 0;
uint256 roundID = 0;

vm.prank(staker1);
core.setStake(GENERAL_COURT, 2000);
vm.prank(disputer);
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase

core.draw(disputeID, iterations);

uint256 iterationsCount;
uint256 disputesWithoutJurors;
if (iterations < DEFAULT_NB_OF_JURORS) {
iterationsCount = iterations;
disputesWithoutJurors = 1;
} else {
iterationsCount = DEFAULT_NB_OF_JURORS;
disputesWithoutJurors = 0;
}

KlerosCore.Round memory round = core.getRoundInfo(disputeID, roundID);
assertEq(round.drawIterations, iterationsCount, "Wrong drawIterations number");
assertEq(round.nbVotes, DEFAULT_NB_OF_JURORS, "Wrong nbVotes");

(uint256 totalStaked, uint256 totalLocked, uint256 stakedInCourt, ) = sortitionModule.getJurorBalance(
staker1,
GENERAL_COURT
);
uint256 pnkAtStake = (minStake * alpha) / ONE_BASIS_POINT;
assertEq(totalStaked, 2000, "Wrong amount total staked");
assertEq(totalLocked, pnkAtStake * iterationsCount, "Wrong amount locked"); // 1000 per draw
assertEq(stakedInCourt, 2000, "Wrong amount staked in court");
assertEq(sortitionModule.disputesWithoutJurors(), disputesWithoutJurors, "Wrong disputesWithoutJurors count");
}

function testFuzz_drawIterations_nbJurors(uint256 iterations, uint256 disputeValue) public {
uint256 disputeID = 0;
uint256 roundID = 0;

uint256 arbitrationCost = core.arbitrationCost(arbitratorExtraData);
// Cap it to 10 eth, so the number of jurors is not astronomical.
vm.assume(disputeValue >= arbitrationCost && disputeValue <= 10 ether);
vm.deal(disputer, 10 ether);

vm.prank(staker1);
core.setStake(GENERAL_COURT, 2000);
vm.prank(disputer);
arbitrable.createDispute{value: disputeValue}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase

core.draw(disputeID, iterations);

KlerosCore.Round memory round = core.getRoundInfo(disputeID, roundID);
assertEq(round.totalFeesForJurors, disputeValue, "Wrong totalFeesForJurors");
assertEq(round.nbVotes, disputeValue / feeForJuror, "Wrong nbVotes");

uint256 iterationsCount;
uint256 disputesWithoutJurors;
if (iterations < round.nbVotes) {
iterationsCount = iterations;
disputesWithoutJurors = 1;
} else {
iterationsCount = round.nbVotes;
disputesWithoutJurors = 0;
}

assertEq(round.drawIterations, iterationsCount, "Wrong drawIterations number");
(uint256 totalStaked, uint256 totalLocked, uint256 stakedInCourt, ) = sortitionModule.getJurorBalance(
staker1,
GENERAL_COURT
);
uint256 pnkAtStake = (minStake * alpha) / ONE_BASIS_POINT;
assertEq(totalStaked, 2000, "Wrong amount total staked");
assertEq(totalLocked, pnkAtStake * iterationsCount, "Wrong amount locked");
assertEq(stakedInCourt, 2000, "Wrong amount staked in court");
assertEq(sortitionModule.disputesWithoutJurors(), disputesWithoutJurors, "Wrong disputesWithoutJurors count");
}
}
Loading
Loading