Skip to content

Commit 78e4a3c

Browse files
committed
refactor: moved the safe transfer and allowance increase functions to a library
1 parent cfaf9f5 commit 78e4a3c

File tree

4 files changed

+71
-101
lines changed

4 files changed

+71
-101
lines changed

contracts/src/arbitration/KlerosCore.sol

Lines changed: 14 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88

99
pragma solidity 0.8.18;
1010

11-
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
1211
import "./IArbitrator.sol";
1312
import "./IDisputeKit.sol";
1413
import "./ISortitionModule.sol";
14+
import "../libraries/SafeERC20.sol";
1515

1616
/// @title KlerosCore
1717
/// Core arbitrator contract for Kleros v2.
1818
/// Note that this contract trusts the PNK token, the dispute kit and the sortition module contracts.
1919
contract KlerosCore is IArbitrator {
20+
using SafeERC20 for IERC20;
21+
2022
// ************************************* //
2123
// * Enums / Structs * //
2224
// ************************************* //
@@ -508,7 +510,7 @@ contract KlerosCore is IArbitrator {
508510
if (!currencyRates[_feeToken].feePaymentAccepted) revert TokenNotAccepted();
509511
if (_feeAmount < arbitrationCost(_extraData, _feeToken)) revert ArbitrationFeesNotEnough();
510512

511-
require(_safeTransferFrom(_feeToken, msg.sender, address(this), _feeAmount), "Transfer failed");
513+
require(_feeToken.safeTransferFrom(msg.sender, address(this), _feeAmount), "Transfer failed");
512514
return _createDispute(_numberOfChoices, _extraData, _feeToken, _feeAmount);
513515
}
514516

@@ -792,9 +794,9 @@ contract KlerosCore is IArbitrator {
792794
payable(governor).send(round.totalFeesForJurors);
793795
} else {
794796
// The dispute fees were paid in ERC20
795-
_safeTransfer(round.feeToken, governor, round.totalFeesForJurors);
797+
round.feeToken.safeTransfer(governor, round.totalFeesForJurors);
796798
}
797-
_safeTransfer(pinakion, governor, _params.pnkPenaltiesInRound);
799+
pinakion.safeTransfer(governor, _params.pnkPenaltiesInRound);
798800
emit LeftoverRewardSent(
799801
_params.disputeID,
800802
_params.round,
@@ -834,21 +836,21 @@ contract KlerosCore is IArbitrator {
834836

835837
// Give back the locked PNKs in case the juror fully unstaked earlier.
836838
if (jurors[account].stakedPnk[dispute.courtID] == 0) {
837-
_safeTransfer(pinakion, account, pnkLocked);
839+
pinakion.safeTransfer(account, pnkLocked);
838840
}
839841

840842
// Transfer the rewards
841843
uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
842844
round.sumPnkRewardPaid += pnkReward;
843845
uint256 feeReward = ((round.totalFeesForJurors / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
844846
round.sumFeeRewardPaid += feeReward;
845-
_safeTransfer(pinakion, account, pnkReward);
847+
pinakion.safeTransfer(account, pnkReward);
846848
if (round.feeToken == NATIVE_CURRENCY) {
847849
// The dispute fees were paid in ETH
848850
payable(account).send(feeReward);
849851
} else {
850852
// The dispute fees were paid in ERC20
851-
_safeTransfer(round.feeToken, account, feeReward);
853+
round.feeToken.safeTransfer(account, feeReward);
852854
}
853855
emit TokenAndETHShift(
854856
account,
@@ -866,15 +868,15 @@ contract KlerosCore is IArbitrator {
866868
uint256 leftoverFeeReward = round.totalFeesForJurors - round.sumFeeRewardPaid;
867869
if (leftoverPnkReward != 0 || leftoverFeeReward != 0) {
868870
if (leftoverPnkReward != 0) {
869-
_safeTransfer(pinakion, governor, leftoverPnkReward);
871+
pinakion.safeTransfer(governor, leftoverPnkReward);
870872
}
871873
if (leftoverFeeReward != 0) {
872874
if (round.feeToken == NATIVE_CURRENCY) {
873875
// The dispute fees were paid in ETH
874876
payable(governor).send(leftoverFeeReward);
875877
} else {
876878
// The dispute fees were paid in ERC20
877-
_safeTransfer(round.feeToken, governor, leftoverFeeReward);
879+
round.feeToken.safeTransfer(governor, leftoverFeeReward);
878880
}
879881
}
880882
emit LeftoverRewardSent(
@@ -1138,7 +1140,7 @@ contract KlerosCore is IArbitrator {
11381140
if (_stake >= currentStake) {
11391141
transferredAmount = _stake - currentStake;
11401142
if (transferredAmount > 0) {
1141-
if (_safeTransferFrom(pinakion, _account, address(this), transferredAmount)) {
1143+
if (pinakion.safeTransferFrom(_account, address(this), transferredAmount)) {
11421144
if (currentStake == 0) {
11431145
juror.courtIDs.push(_courtID);
11441146
}
@@ -1151,7 +1153,7 @@ contract KlerosCore is IArbitrator {
11511153
// Keep locked PNKs in the contract and release them after dispute is executed.
11521154
transferredAmount = currentStake - juror.lockedPnk[_courtID] - _penalty;
11531155
if (transferredAmount > 0) {
1154-
if (_safeTransfer(pinakion, _account, transferredAmount)) {
1156+
if (pinakion.safeTransfer(_account, transferredAmount)) {
11551157
for (uint256 i = juror.courtIDs.length; i > 0; i--) {
11561158
if (juror.courtIDs[i - 1] == _courtID) {
11571159
juror.courtIDs[i - 1] = juror.courtIDs[juror.courtIDs.length - 1];
@@ -1166,7 +1168,7 @@ contract KlerosCore is IArbitrator {
11661168
} else {
11671169
transferredAmount = currentStake - _stake - _penalty;
11681170
if (transferredAmount > 0) {
1169-
if (!_safeTransfer(pinakion, _account, transferredAmount)) {
1171+
if (!pinakion.safeTransfer(_account, transferredAmount)) {
11701172
return false;
11711173
}
11721174
}
@@ -1214,38 +1216,6 @@ contract KlerosCore is IArbitrator {
12141216
}
12151217
}
12161218

1217-
/// @dev Calls transfer() without reverting.
1218-
/// @param _token Token to transfer.
1219-
/// @param _to Recepient address.
1220-
/// @param _value Amount transferred.
1221-
/// @return Whether transfer succeeded or not.
1222-
function _safeTransfer(IERC20 _token, address _to, uint256 _value) internal returns (bool) {
1223-
(bool success, bytes memory data) = address(_token).call(abi.encodeCall(IERC20.transfer, (_to, _value)));
1224-
return (success && (data.length == 0 || abi.decode(data, (bool))));
1225-
}
1226-
1227-
/// @dev Calls transferFrom() without reverting.
1228-
/// @param _token Token to transfer.
1229-
/// @param _from Sender address.
1230-
/// @param _to Recepient address.
1231-
/// @param _value Amount transferred.
1232-
/// @return Whether transfer succeeded or not.
1233-
function _safeTransferFrom(IERC20 _token, address _from, address _to, uint256 _value) internal returns (bool) {
1234-
(bool success, bytes memory data) = address(_token).call(
1235-
abi.encodeCall(IERC20.transferFrom, (_from, _to, _value))
1236-
);
1237-
return (success && (data.length == 0 || abi.decode(data, (bool))));
1238-
}
1239-
1240-
/// @dev Increases the allowance granted to `spender` by the caller.
1241-
/// @param _token Token to transfer.
1242-
/// @param _spender The address which will spend the funds.
1243-
/// @param _addedValue The amount of tokens to increase the allowance by.
1244-
function _increaseAllowance(IERC20 _token, address _spender, uint256 _addedValue) public virtual returns (bool) {
1245-
_token.approve(_spender, _token.allowance(address(this), _spender) + _addedValue);
1246-
return true;
1247-
}
1248-
12491219
// ************************************* //
12501220
// * Errors * //
12511221
// ************************************* //

contracts/src/arbitration/arbitrables/ArbitrableExample.sol

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
pragma solidity 0.8.18;
44

5-
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
65
import "../IArbitrable.sol";
76
import "../../evidence/IMetaEvidence.sol";
7+
import "../../libraries/SafeERC20.sol";
88

99
/// @title ArbitrableExample
1010
/// An example of an arbitrable contract which connects to the arbitator that implements the updated interface.
1111
contract ArbitrableExample is IArbitrable, IMetaEvidence {
12+
using SafeERC20 for IERC20;
13+
1214
// ************************************* //
1315
// * Enums / Structs * //
1416
// ************************************* //
@@ -106,8 +108,8 @@ contract ArbitrableExample is IArbitrable, IMetaEvidence {
106108
uint256 localDisputeID = disputes.length;
107109
disputes.push(DisputeStruct({isRuled: false, ruling: 0, numberOfRulingOptions: _numberOfRulingOptions}));
108110

109-
require(_safeTransferFrom(weth, msg.sender, address(this), _feeInWeth), "Transfer failed");
110-
require(_increaseAllowance(weth, address(arbitrator), _feeInWeth), "Allowance increase failed");
111+
require(weth.safeTransferFrom(msg.sender, address(this), _feeInWeth), "Transfer failed");
112+
require(weth.increaseAllowance(address(arbitrator), _feeInWeth), "Allowance increase failed");
111113

112114
disputeID = arbitrator.createDispute(_numberOfRulingOptions, _arbitratorExtraData, weth, _feeInWeth);
113115
externalIDtoLocalID[disputeID] = localDisputeID;
@@ -130,30 +132,4 @@ contract ArbitrableExample is IArbitrable, IMetaEvidence {
130132

131133
emit Ruling(IArbitrator(msg.sender), _externalDisputeID, dispute.ruling);
132134
}
133-
134-
// ************************************* //
135-
// * Internal * //
136-
// ************************************* //
137-
138-
/// @dev Increases the allowance granted to `spender` by the caller.
139-
/// @param _token Token to transfer.
140-
/// @param _spender The address which will spend the funds.
141-
/// @param _addedValue The amount of tokens to increase the allowance by.
142-
function _increaseAllowance(IERC20 _token, address _spender, uint256 _addedValue) public virtual returns (bool) {
143-
_token.approve(_spender, _token.allowance(address(this), _spender) + _addedValue);
144-
return true;
145-
}
146-
147-
/// @dev Calls transferFrom() without reverting.
148-
/// @param _token Token to transfer.
149-
/// @param _from Sender address.
150-
/// @param _to Recepient address.
151-
/// @param _value Amount transferred.
152-
/// @return Whether transfer succeeded or not.
153-
function _safeTransferFrom(IERC20 _token, address _from, address _to, uint256 _value) internal returns (bool) {
154-
(bool success, bytes memory data) = address(_token).call(
155-
abi.encodeCall(IERC20.transferFrom, (_from, _to, _value))
156-
);
157-
return (success && (data.length == 0 || abi.decode(data, (bool))));
158-
}
159135
}

contracts/src/gateway/HomeGateway.sol

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ pragma solidity 0.8.18;
1111
import "../arbitration/IArbitrator.sol";
1212
import "./interfaces/IForeignGateway.sol";
1313
import "./interfaces/IHomeGateway.sol";
14+
import "../libraries/SafeERC20.sol";
1415

1516
/// Home Gateway
1617
/// Counterpart of `ForeignGateway`
1718
contract HomeGateway is IHomeGateway {
19+
using SafeERC20 for IERC20;
20+
1821
// ************************************* //
1922
// * Enums / Structs * //
2023
// ************************************* //
@@ -165,8 +168,8 @@ contract HomeGateway is IHomeGateway {
165168
RelayedData storage relayedData = disputeHashtoRelayedData[disputeHash];
166169
require(relayedData.relayer == address(0), "Dispute already relayed");
167170

168-
require(_safeTransferFrom(feeToken, msg.sender, address(this), _feeAmount), "Transfer failed");
169-
require(_increaseAllowance(feeToken, address(arbitrator), _feeAmount), "Allowance increase failed");
171+
require(feeToken.safeTransferFrom(msg.sender, address(this), _feeAmount), "Transfer failed");
172+
require(feeToken.increaseAllowance(address(arbitrator), _feeAmount), "Allowance increase failed");
170173

171174
uint256 disputeID = arbitrator.createDispute(_choices, _extraData, feeToken, _feeAmount);
172175
disputeIDtoHash[disputeID] = disputeHash;
@@ -208,30 +211,4 @@ contract HomeGateway is IHomeGateway {
208211
function acceptedFeeToken() external view returns (IERC20) {
209212
return feeToken;
210213
}
211-
212-
// ************************************* //
213-
// * Internal * //
214-
// ************************************* //
215-
216-
/// @dev Increases the allowance granted to `spender` by the caller.
217-
/// @param _token Token to transfer.
218-
/// @param _spender The address which will spend the funds.
219-
/// @param _addedValue The amount of tokens to increase the allowance by.
220-
function _increaseAllowance(IERC20 _token, address _spender, uint256 _addedValue) public virtual returns (bool) {
221-
_token.approve(_spender, _token.allowance(address(this), _spender) + _addedValue);
222-
return true;
223-
}
224-
225-
/// @dev Calls transferFrom() without reverting.
226-
/// @param _token Token to transfer.
227-
/// @param _from Sender address.
228-
/// @param _to Recepient address.
229-
/// @param _value Amount transferred.
230-
/// @return Whether transfer succeeded or not.
231-
function _safeTransferFrom(IERC20 _token, address _from, address _to, uint256 _value) internal returns (bool) {
232-
(bool success, bytes memory data) = address(_token).call(
233-
abi.encodeCall(IERC20.transferFrom, (_from, _to, _value))
234-
);
235-
return (success && (data.length == 0 || abi.decode(data, (bool))));
236-
}
237214
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: MIT
2+
// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/a7a94c77463acea95d979aae1580fb0ddc3b6a1e/contracts/token/ERC20/utils/SafeERC20.sol
3+
4+
pragma solidity ^0.8.18;
5+
6+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7+
8+
/// @title SafeERC20
9+
/// @dev Wrappers around ERC20 operations that throw on failure (when the token
10+
/// contract returns false). Tokens that return no value (and instead revert or
11+
/// throw on failure) are also supported, non-reverting calls are assumed to be
12+
/// successful.
13+
/// To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
14+
/// which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
15+
library SafeERC20 {
16+
/// @dev Increases the allowance granted to `spender` by the caller.
17+
/// @param _token Token to transfer.
18+
/// @param _spender The address which will spend the funds.
19+
/// @param _addedValue The amount of tokens to increase the allowance by.
20+
function increaseAllowance(IERC20 _token, address _spender, uint256 _addedValue) internal returns (bool) {
21+
_token.approve(_spender, _token.allowance(address(this), _spender) + _addedValue);
22+
return true;
23+
}
24+
25+
/// @dev Calls transfer() without reverting.
26+
/// @param _token Token to transfer.
27+
/// @param _to Recepient address.
28+
/// @param _value Amount transferred.
29+
/// @return Whether transfer succeeded or not.
30+
function safeTransfer(IERC20 _token, address _to, uint256 _value) internal returns (bool) {
31+
(bool success, bytes memory data) = address(_token).call(abi.encodeCall(IERC20.transfer, (_to, _value)));
32+
return (success && (data.length == 0 || abi.decode(data, (bool))));
33+
}
34+
35+
/// @dev Calls transferFrom() without reverting.
36+
/// @param _token Token to transfer.
37+
/// @param _from Sender address.
38+
/// @param _to Recepient address.
39+
/// @param _value Amount transferred.
40+
/// @return Whether transfer succeeded or not.
41+
function safeTransferFrom(IERC20 _token, address _from, address _to, uint256 _value) internal returns (bool) {
42+
(bool success, bytes memory data) = address(_token).call(
43+
abi.encodeCall(IERC20.transferFrom, (_from, _to, _value))
44+
);
45+
return (success && (data.length == 0 || abi.decode(data, (bool))));
46+
}
47+
}

0 commit comments

Comments
 (0)