Skip to content

Commit a016740

Browse files
feat(KC): add event for failed transfer
1 parent 8c256aa commit a016740

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

contracts/src/libraries/SafeERC20.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
1515
/// To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
1616
/// which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
1717
library SafeERC20 {
18+
/// @notice Emits when safeTransfer fails.
19+
/// @param _token Token to transfer.
20+
/// @param _to Recipient address.
21+
/// @param _value Amount transferred.
22+
event SafeTransferFailed(IERC20 _token, address _to, uint256 _value);
23+
1824
/// @notice Increases the allowance granted to `spender` by the caller.
1925
/// @param _token Token to transfer.
2026
/// @param _spender The address which will spend the funds.
@@ -31,7 +37,9 @@ library SafeERC20 {
3137
/// @return Whether transfer succeeded or not.
3238
function safeTransfer(IERC20 _token, address _to, uint256 _value) internal returns (bool) {
3339
(bool success, bytes memory data) = address(_token).call(abi.encodeCall(IERC20.transfer, (_to, _value)));
34-
return (success && (data.length == 0 || abi.decode(data, (bool))));
40+
bool ok = success && (data.length == 0 || abi.decode(data, (bool)));
41+
if (!ok) emit SafeTransferFailed(_token, _to, _value);
42+
return ok;
3543
}
3644

3745
/// @notice Calls transferFrom() without reverting.

contracts/test/foundry/KlerosCore_Execution.t.sol

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.24;
33

44
import {KlerosCore_TestBase} from "./KlerosCore_TestBase.sol";
5-
import {KlerosCore} from "../../src/arbitration/KlerosCore.sol";
5+
import {KlerosCore, SafeERC20} from "../../src/arbitration/KlerosCore.sol";
66
import {SortitionModule} from "../../src/arbitration/SortitionModule.sol";
77
import {DisputeKitClassicBase} from "../../src/arbitration/dispute-kits/DisputeKitClassicBase.sol";
88
import {IArbitratorV2, IArbitrableV2} from "../../src/arbitration/KlerosCore.sol";
@@ -543,7 +543,7 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase {
543543
voteIDs[1] = 1;
544544
voteIDs[2] = 2;
545545
vm.prank(staker1);
546-
disputeKit.castVote(disputeID, voteIDs, 1, 0, "XYZ"); // Staker1 only got 1 vote because of low stake
546+
disputeKit.castVote(disputeID, voteIDs, 1, 0, "XYZ");
547547

548548
core.passPeriod(disputeID); // Appeal
549549

@@ -563,6 +563,59 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase {
563563
assertEq(feeToken.balanceOf(disputer), 0.82 ether, "Wrong fee token balance of disputer");
564564
}
565565

566+
function test_execute_feeToken_failedTransfer() public {
567+
uint256 disputeID = 0;
568+
569+
feeToken.transfer(disputer, 1 ether);
570+
vm.prank(disputer);
571+
feeToken.approve(address(arbitrable), 1 ether);
572+
573+
vm.prank(owner);
574+
core.changeAcceptedFeeTokens(feeToken, true);
575+
vm.prank(owner);
576+
core.changeCurrencyRates(feeToken, 500, 3);
577+
578+
vm.prank(disputer);
579+
arbitrable.createDispute("Action", 0.18 ether);
580+
581+
vm.prank(staker1);
582+
core.setStake(GENERAL_COURT, 20000);
583+
vm.warp(block.timestamp + minStakingTime);
584+
sortitionModule.passPhase(); // Generating
585+
vm.warp(block.timestamp + rngLookahead);
586+
sortitionModule.passPhase(); // Drawing phase
587+
588+
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
589+
590+
vm.warp(block.timestamp + timesPerPeriod[0]);
591+
core.passPeriod(disputeID); // Vote
592+
593+
uint256[] memory voteIDs = new uint256[](3);
594+
voteIDs[0] = 0;
595+
voteIDs[1] = 1;
596+
voteIDs[2] = 2;
597+
vm.prank(staker1);
598+
disputeKit.castVote(disputeID, voteIDs, 1, 0, "XYZ");
599+
600+
core.passPeriod(disputeID); // Appeal
601+
602+
vm.warp(block.timestamp + timesPerPeriod[3]);
603+
core.passPeriod(disputeID); // Execution
604+
605+
vm.prank(address(core));
606+
feeToken.transfer(disputer, 0.18 ether); // Manually send all balance to make rewards fail
607+
assertEq(feeToken.balanceOf(staker1), 0, "Wrong fee token balance of staker1");
608+
assertEq(feeToken.balanceOf(disputer), 1 ether, "Wrong fee token balance of disputer");
609+
610+
vm.expectEmit(true, true, true, true);
611+
emit SafeERC20.SafeTransferFailed(feeToken, staker1, 0.06 ether); // One failed iteration has 0.06 eth
612+
core.execute(disputeID, 0, 6);
613+
614+
KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
615+
assertEq(round.repartitions, 6, "Wrong repartitions");
616+
assertEq(feeToken.balanceOf(staker1), 0, "Staker1 still has no balance");
617+
}
618+
566619
function test_execute_NoCoherence_feeToken() public {
567620
uint256 disputeID = 0;
568621

0 commit comments

Comments
 (0)