Skip to content

Commit c97270a

Browse files
authored
Merge pull request #2135 from kleros/fix/no-zero-amount-shift
Core fix: do not emit TokenAndETHShift for zero amounts, no zero transfer
2 parents 7c30463 + bd983ca commit c97270a

File tree

4 files changed

+62
-60
lines changed

4 files changed

+62
-60
lines changed

contracts/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ The format is based on [Common Changelog](https://common-changelog.org/).
5454
### Fixed
5555

5656
- Do not pass to Voting period if all the commits are cast because it breaks the current Shutter auto-reveal process. ([#2085](https://github.com/kleros/kleros-v2/issues/2085))
57+
- Do not emit the `KlerosCore.TokenAndETHShift` event if the both the PNK and fee amounts are zero ([#2135](https://github.com/kleros/kleros-v2/issues/2135))
58+
- Do not make PNK or ETH transfers if the amounts are zero ([#2135](https://github.com/kleros/kleros-v2/issues/2135))
5759

5860
## [0.12.0] - 2025-08-05
5961

contracts/src/arbitration/KlerosCore.sol

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -925,17 +925,19 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
925925
penalizedInCourtID,
926926
penalty
927927
);
928-
_params.pnkPenaltiesInRound += availablePenalty;
929-
emit TokenAndETHShift(
930-
account,
931-
_params.disputeID,
932-
_params.round,
933-
coherence,
934-
0,
935-
-int256(availablePenalty),
936-
0,
937-
round.feeToken
938-
);
928+
if (availablePenalty != 0) {
929+
_params.pnkPenaltiesInRound += availablePenalty;
930+
emit TokenAndETHShift(
931+
account,
932+
_params.disputeID,
933+
_params.round,
934+
coherence,
935+
0,
936+
-int256(availablePenalty),
937+
0,
938+
round.feeToken
939+
);
940+
}
939941

940942
if (pnkBalance == 0 || !disputeKit.isVoteActive(_params.disputeID, _params.round, _params.repartition)) {
941943
// The juror is inactive or their balance is can't cover penalties anymore, unstake them from all courts.
@@ -996,24 +998,28 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
996998
uint256 feeReward = _applyCoherence(round.totalFeesForJurors / _params.coherentCount, feeCoherence);
997999
round.sumFeeRewardPaid += feeReward;
9981000

999-
// Transfer the fee reward
1000-
_transferFeeToken(round.feeToken, payable(account), feeReward);
1001-
1002-
// Stake the PNK reward if possible, bypasses delayed stakes and other checks done by validateStake()
1003-
if (!sortitionModule.setStakeReward(account, dispute.courtID, pnkReward)) {
1004-
pinakion.safeTransfer(account, pnkReward);
1001+
if (feeReward != 0) {
1002+
// Transfer the fee reward
1003+
_transferFeeToken(round.feeToken, payable(account), feeReward);
1004+
}
1005+
if (pnkReward != 0) {
1006+
// Stake the PNK reward if possible, bypasses delayed stakes and other checks done by validateStake()
1007+
if (!sortitionModule.setStakeReward(account, dispute.courtID, pnkReward)) {
1008+
pinakion.safeTransfer(account, pnkReward);
1009+
}
1010+
}
1011+
if (pnkReward != 0 || feeReward != 0) {
1012+
emit TokenAndETHShift(
1013+
account,
1014+
_params.disputeID,
1015+
_params.round,
1016+
pnkCoherence,
1017+
feeCoherence,
1018+
int256(pnkReward),
1019+
int256(feeReward),
1020+
round.feeToken
1021+
);
10051022
}
1006-
1007-
emit TokenAndETHShift(
1008-
account,
1009-
_params.disputeID,
1010-
_params.round,
1011-
pnkCoherence,
1012-
feeCoherence,
1013-
int256(pnkReward),
1014-
int256(feeReward),
1015-
round.feeToken
1016-
);
10171023

10181024
// Transfer any residual rewards to the owner. It may happen due to partial coherence of the jurors.
10191025
if (_params.repartition == _params.numberOfVotesInRound * 2 - 1) {

contracts/test/foundry/KlerosCore_Execution.t.sol

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,6 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase {
103103
// Check iterations for the winning staker to see the shifts
104104
vm.expectEmit(true, true, true, true);
105105
emit SortitionModule.StakeLocked(staker2, 0, true);
106-
vm.expectEmit(true, true, true, true);
107-
emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 0, 0, 0, IERC20(address(0))); // penalties but amounts are 0
108-
vm.expectEmit(true, true, true, true);
109-
emit SortitionModule.StakeLocked(staker2, 0, true);
110-
vm.expectEmit(true, true, true, true);
111-
emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 0, 0, 0, IERC20(address(0))); // penalties but amounts are 0
112106
core.execute(disputeID, 0, 3); // Do 3 iterations to check penalties first
113107

114108
(uint256 totalStaked, uint256 totalLocked, , ) = sortitionModule.getJurorBalance(staker1, GENERAL_COURT);
@@ -123,8 +117,6 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase {
123117

124118
vm.expectEmit(true, true, true, true);
125119
emit SortitionModule.StakeLocked(staker1, 0, true);
126-
vm.expectEmit(true, true, true, true);
127-
emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 0, 0, 0, 0, IERC20(address(0))); // rewards but amounts are 0
128120
// Check iterations for the winning staker to see the shifts
129121
vm.expectEmit(true, true, true, true);
130122
emit SortitionModule.StakeLocked(staker2, 1000, true);
@@ -538,8 +530,6 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase {
538530

539531
// Check only once per penalty and per reward
540532
vm.expectEmit(true, true, true, true);
541-
emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 10000, 0, 0, 0, feeToken); // penalties but amounts are 0
542-
vm.expectEmit(true, true, true, true);
543533
emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 10000, 10000, 0, 0.06 ether, feeToken); // rewards
544534
core.execute(disputeID, 0, 6);
545535

contracts/test/integration/index.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -131,23 +131,23 @@ describe("Integration tests", async () => {
131131
throw new Error("Block hash is null - cannot calculate dispute hash");
132132
}
133133
// Relayer tx
134-
const tx2 = await homeGateway
135-
.connect(relayer)
136-
["relayCreateDispute((bytes32,uint256,address,uint256,uint256,uint256,uint256,bytes))"](
137-
{
138-
foreignBlockHash: ethers.toBeHex(lastBlock.hash),
139-
foreignChainID: 31337,
140-
foreignArbitrable: arbitrable.target,
141-
foreignDisputeID: disputeId,
142-
externalDisputeID: ethers.keccak256(ethers.toUtf8Bytes("future of france")),
143-
templateId: 0,
144-
choices: 2,
145-
extraData: "0x00",
146-
},
147-
{ value: arbitrationCost }
148-
);
149-
expect(tx2).to.emit(homeGateway, "DisputeRequest");
150-
await tx2.wait();
134+
await expect(
135+
homeGateway
136+
.connect(relayer)
137+
["relayCreateDispute((bytes32,uint256,address,uint256,uint256,uint256,uint256,bytes))"](
138+
{
139+
foreignBlockHash: ethers.toBeHex(lastBlock.hash),
140+
foreignChainID: 31337,
141+
foreignArbitrable: arbitrable.target,
142+
foreignDisputeID: disputeId,
143+
externalDisputeID: ethers.keccak256(ethers.toUtf8Bytes("future of france")),
144+
templateId: 0,
145+
choices: 2,
146+
extraData: "0x00",
147+
},
148+
{ value: arbitrationCost }
149+
)
150+
).to.emit(homeGateway, "DisputeRequest");
151151

152152
await network.provider.send("evm_increaseTime", [2000]); // Wait for minStakingTime
153153
await network.provider.send("evm_mine");
@@ -186,12 +186,16 @@ describe("Integration tests", async () => {
186186

187187
await core.passPeriod(0);
188188
expect((await core.disputes(0)).period).to.equal(Period.execution);
189-
expect(await core.execute(0, 0, 1000)).to.emit(core, "TokenAndETHShift");
190-
191-
const tx4 = await core.executeRuling(0, { gasLimit: 10000000, gasPrice: 5000000000 });
189+
await expect(core.execute(0, 0, 1000))
190+
.to.emit(core, "TokenAndETHShift")
191+
.withArgs(deployer, 0, 0, 10000, 10000, 0, arbitrationCost / 3n, ethers.ZeroAddress);
192+
193+
await expect(core.executeRuling(0, { gasLimit: 10000000, gasPrice: 5000000000 }))
194+
.to.emit(core, "Ruling")
195+
.withArgs(homeGateway.target, 0, 0)
196+
.and.to.emit(arbitrable, "Ruling")
197+
.withArgs(foreignGateway.target, 1, 0); // The ForeignGateway starts counting disputeID from 1.
192198
console.log("Ruling executed on KlerosCore");
193-
expect(tx4).to.emit(core, "Ruling").withArgs(homeGateway.target, 0, 0);
194-
expect(tx4).to.emit(arbitrable, "Ruling").withArgs(foreignGateway.target, 1, 0); // The ForeignGateway starts counting disputeID from 1.
195199
});
196200

197201
const mineBlocks = async (n: number) => {

0 commit comments

Comments
 (0)