migration to 1inch UniversalERC20

This commit is contained in:
andyk 2020-06-02 16:49:24 +03:00
parent a683c24dec
commit fea677a607
7 changed files with 188 additions and 87 deletions

View File

@ -5,6 +5,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../libraries/EthAddressLib.sol"; import "../libraries/EthAddressLib.sol";
import "../libraries/UniversalERC20.sol";
import "../mocks/tokens/MintableERC20.sol"; import "../mocks/tokens/MintableERC20.sol";
/// @title MockKyberProxy /// @title MockKyberProxy
@ -16,6 +17,7 @@ import "../mocks/tokens/MintableERC20.sol";
contract MockKyberProxy { contract MockKyberProxy {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
using SafeERC20 for MintableERC20; using SafeERC20 for MintableERC20;
using UniversalERC20 for IERC20;
/// @notice The token which the msg.sender of tradeWithHint will burn /// @notice The token which the msg.sender of tradeWithHint will burn
MintableERC20 public tokenToBurn; MintableERC20 public tokenToBurn;
@ -31,12 +33,12 @@ contract MockKyberProxy {
IERC20 _toToken, IERC20 _toToken,
address _receiver, address _receiver,
uint256 _maxAmount, uint256 _maxAmount,
uint minConversionRate, uint256 minConversionRate,
address _referral, address _referral,
bytes calldata _filtering bytes calldata _filtering
) external payable returns(uint256) { ) external payable returns (uint256) {
require(tokenToBurn.mint(1 ether), "TRADE_WITH_HINT. Reverted mint()"); require(tokenToBurn.mint(1 ether), "TRADE_WITH_HINT. Reverted mint()");
if (address(_fromToken) != EthAddressLib.ethAddress()) { if (!_fromToken.isETH()) {
_fromToken.safeTransferFrom(msg.sender, address(this), _amount); _fromToken.safeTransferFrom(msg.sender, address(this), _amount);
} }
tokenToBurn.safeTransfer(msg.sender, 1 ether); tokenToBurn.safeTransfer(msg.sender, 1 ether);

View File

@ -8,10 +8,12 @@ import "../libraries/EthAddressLib.sol";
import "../mocks/tokens/MintableERC20.sol"; import "../mocks/tokens/MintableERC20.sol";
import "../interfaces/IOneSplit.sol"; import "../interfaces/IOneSplit.sol";
import "../libraries/UniversalERC20.sol";
contract MockOneSplit is IOneSplit { contract MockOneSplit is IOneSplit {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
using SafeERC20 for MintableERC20; using SafeERC20 for MintableERC20;
using UniversalERC20 for IERC20;
MintableERC20 public tokenToBurn; MintableERC20 public tokenToBurn;
@ -55,7 +57,7 @@ contract MockOneSplit is IOneSplit {
uint256 disableFlags uint256 disableFlags
) public override payable { ) public override payable {
require(tokenToBurn.mint(10000 ether), "TRADE_WITH_HINT. Reverted mint()"); require(tokenToBurn.mint(10000 ether), "TRADE_WITH_HINT. Reverted mint()");
if (address(fromToken) != EthAddressLib.ethAddress()) { if (!fromToken.isETH()) {
fromToken.safeTransferFrom(msg.sender, address(this), amount); fromToken.safeTransferFrom(msg.sender, address(this), amount);
} }
tokenToBurn.safeTransfer(msg.sender, 10000 ether); tokenToBurn.safeTransfer(msg.sender, 10000 ether);

View File

@ -11,6 +11,7 @@ import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../interfaces/IKyberNetworkProxyInterface.sol"; import "../interfaces/IKyberNetworkProxyInterface.sol";
import "../interfaces/IExchangeAdapter.sol"; import "../interfaces/IExchangeAdapter.sol";
import "../libraries/EthAddressLib.sol"; import "../libraries/EthAddressLib.sol";
import "../libraries/UniversalERC20.sol";
/// @title TokenDistributor /// @title TokenDistributor
@ -26,6 +27,7 @@ import "../libraries/EthAddressLib.sol";
contract TokenDistributor is ReentrancyGuard, VersionedInitializable { contract TokenDistributor is ReentrancyGuard, VersionedInitializable {
using SafeMath for uint256; using SafeMath for uint256;
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
using UniversalERC20 for IERC20;
struct Distribution { struct Distribution {
address[] receivers; address[] receivers;
@ -104,9 +106,8 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable {
/// @param _tokens list of ERC20 tokens to distribute /// @param _tokens list of ERC20 tokens to distribute
function distribute(IERC20[] memory _tokens) public { function distribute(IERC20[] memory _tokens) public {
for (uint256 i = 0; i < _tokens.length; i++) { for (uint256 i = 0; i < _tokens.length; i++) {
uint256 _balanceToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress()) uint256 _balanceToDistribute = _tokens[i].universalBalanceOf(address(this));
? _tokens[i].balanceOf(address(this))
: address(this).balance;
if (_balanceToDistribute <= 0) { if (_balanceToDistribute <= 0) {
continue; continue;
} }
@ -129,9 +130,8 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable {
/// @param _percentages list of percentages to distribute per token /// @param _percentages list of percentages to distribute per token
function distributeWithPercentages(IERC20[] memory _tokens, uint256[] memory _percentages) public { function distributeWithPercentages(IERC20[] memory _tokens, uint256[] memory _percentages) public {
for (uint256 i = 0; i < _tokens.length; i++) { for (uint256 i = 0; i < _tokens.length; i++) {
uint256 _amountToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress()) uint256 _amountToDistribute = _tokens[i].universalBalanceOf(address(this)).mul(_percentages[i]).div(100);
? _tokens[i].balanceOf(address(this)).mul(_percentages[i]).div(100)
: address(this).balance.mul(_percentages[i]).div(100);
if (_amountToDistribute <= 0) { if (_amountToDistribute <= 0) {
continue; continue;
} }
@ -166,13 +166,7 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable {
} }
if (_distribution.receivers[j] != address(0)) { if (_distribution.receivers[j] != address(0)) {
if (_tokenAddress != EthAddressLib.ethAddress()) { _token.universalTransfer(_distribution.receivers[j], _amount);
_token.safeTransfer(_distribution.receivers[j], _amount);
} else {
//solium-disable-next-line
(bool _success,) = _distribution.receivers[j].call{value: _amount}("");
require(_success, "Reverted ETH transfer");
}
emit Distributed(_distribution.receivers[j], _distribution.percentages[j], _amount); emit Distributed(_distribution.receivers[j], _distribution.percentages[j], _amount);
} else { } else {
uint256 _amountToBurn = _amount; uint256 _amountToBurn = _amount;

View File

@ -7,10 +7,12 @@ import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../interfaces/IFlashLoanReceiver.sol"; import "../interfaces/IFlashLoanReceiver.sol";
import "../../interfaces/ILendingPoolAddressesProvider.sol"; import "../../interfaces/ILendingPoolAddressesProvider.sol";
import "../../libraries/EthAddressLib.sol"; import "../../libraries/EthAddressLib.sol";
import "../../libraries/UniversalERC20.sol";
abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
using UniversalERC20 for IERC20;
using SafeMath for uint256; using SafeMath for uint256;
ILendingPoolAddressesProvider public addressesProvider; ILendingPoolAddressesProvider public addressesProvider;
@ -25,28 +27,14 @@ abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
address payable core = addressesProvider.getLendingPoolCore(); address payable core = addressesProvider.getLendingPoolCore();
transferInternal(core,_reserve, _amount); transferInternal(core, _reserve, _amount);
} }
function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal { function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal {
if(_reserve == EthAddressLib.ethAddress()) { IERC20(_reserve).universalTransfer(_destination, _amount);
//solium-disable-next-line
_destination.call{value: _amount}("");
return;
}
IERC20(_reserve).safeTransfer(_destination, _amount);
} }
function getBalanceInternal(address _target, address _reserve) internal view returns(uint256) { function getBalanceInternal(address _target, address _reserve) internal view returns(uint256) {
if(_reserve == EthAddressLib.ethAddress()) { return IERC20(_reserve).universalBalanceOf(_target);
return _target.balance;
}
return IERC20(_reserve).balanceOf(_target);
} }
} }

View File

@ -18,6 +18,7 @@ import "./LendingPoolCore.sol";
import "./LendingPoolDataProvider.sol"; import "./LendingPoolDataProvider.sol";
import "./LendingPoolLiquidationManager.sol"; import "./LendingPoolLiquidationManager.sol";
import "../libraries/EthAddressLib.sol"; import "../libraries/EthAddressLib.sol";
import "../libraries/UniversalERC20.sol";
/** /**
* @title LendingPool contract * @title LendingPool contract
@ -29,6 +30,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
using SafeMath for uint256; using SafeMath for uint256;
using WadRayMath for uint256; using WadRayMath for uint256;
using Address for address; using Address for address;
using UniversalERC20 for IERC20;
LendingPoolAddressesProvider public addressesProvider; LendingPoolAddressesProvider public addressesProvider;
LendingPoolCore public core; LendingPoolCore public core;
@ -849,9 +851,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
{ {
//check that the reserve has enough available liquidity //check that the reserve has enough available liquidity
//we avoid using the getAvailableLiquidity() function in LendingPoolCore to save gas //we avoid using the getAvailableLiquidity() function in LendingPoolCore to save gas
uint256 availableLiquidityBefore = _reserve == EthAddressLib.ethAddress() uint256 availableLiquidityBefore = IERC20(_reserve).universalBalanceOf(address(core));
? address(core).balance
: IERC20(_reserve).balanceOf(address(core));
require( require(
availableLiquidityBefore >= _amount, availableLiquidityBefore >= _amount,
@ -882,9 +882,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable {
receiver.executeOperation(_reserve, _amount, amountFee, _params); receiver.executeOperation(_reserve, _amount, amountFee, _params);
//check that the actual balance of the core contract includes the returned amount //check that the actual balance of the core contract includes the returned amount
uint256 availableLiquidityAfter = _reserve == EthAddressLib.ethAddress() uint256 availableLiquidityAfter = IERC20(_reserve).universalBalanceOf(address(core));
? address(core).balance
: IERC20(_reserve).balanceOf(address(core));
require( require(
availableLiquidityAfter == availableLiquidityBefore.add(amountFee), availableLiquidityAfter == availableLiquidityBefore.add(amountFee),

View File

@ -2,7 +2,6 @@
pragma solidity ^0.6.8; pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/Address.sol";
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol"; import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
@ -14,6 +13,7 @@ import "../interfaces/IReserveInterestRateStrategy.sol";
import "../libraries/WadRayMath.sol"; import "../libraries/WadRayMath.sol";
import "../tokenization/AToken.sol"; import "../tokenization/AToken.sol";
import "../libraries/EthAddressLib.sol"; import "../libraries/EthAddressLib.sol";
import "../libraries/UniversalERC20.sol";
/** /**
* @title LendingPoolCore contract * @title LendingPoolCore contract
@ -29,10 +29,9 @@ contract LendingPoolCore is VersionedInitializable {
using WadRayMath for uint256; using WadRayMath for uint256;
using CoreLibrary for CoreLibrary.ReserveData; using CoreLibrary for CoreLibrary.ReserveData;
using CoreLibrary for CoreLibrary.UserReserveData; using CoreLibrary for CoreLibrary.UserReserveData;
using SafeERC20 for IERC20; using UniversalERC20 for IERC20;
using Address for address payable; using Address for address payable;
/** /**
* @dev DEPRECATED: This event was used in previous LendingPoolCore implementations, and it has been replaced by ReserveDataUpdated() * @dev DEPRECATED: This event was used in previous LendingPoolCore implementations, and it has been replaced by ReserveDataUpdated()
* @param reserve the address of the reserve * @param reserve the address of the reserve
@ -51,7 +50,6 @@ contract LendingPoolCore is VersionedInitializable {
uint256 variableBorrowIndex uint256 variableBorrowIndex
); );
/** /**
* @dev Emitted when the state of a reserve is updated * @dev Emitted when the state of a reserve is updated
* @dev NOTE: This event replaces the Deprecated ReserveUpdated() event, which didn't emit the average stable borrow rate * @dev NOTE: This event replaces the Deprecated ReserveUpdated() event, which didn't emit the average stable borrow rate
@ -362,7 +360,6 @@ contract LendingPoolCore is VersionedInitializable {
_collateralToLiquidate.add(_liquidatedCollateralForFee) _collateralToLiquidate.add(_liquidatedCollateralForFee)
); );
} }
} }
/** /**
@ -422,13 +419,7 @@ contract LendingPoolCore is VersionedInitializable {
external external
onlyLendingPool onlyLendingPool
{ {
if (_reserve != EthAddressLib.ethAddress()) { IERC20(_reserve).universalTransfer(_user, _amount);
IERC20(_reserve).safeTransfer(_user, _amount);
} else {
//solium-disable-next-line
(bool result, ) = _user.call{value: _amount, gas: 50000}("");
require(result, "Transfer of ETH failed");
}
} }
/** /**
@ -443,22 +434,27 @@ contract LendingPoolCore is VersionedInitializable {
address _token, address _token,
address _user, address _user,
uint256 _amount, uint256 _amount,
address _destination address _feeAddress
) external payable onlyLendingPool { ) external payable onlyLendingPool {
address payable feeAddress = address(uint160(_destination)); //cast the address to payable IERC20 token = IERC20(_token);
if (_token != EthAddressLib.ethAddress()) { if (!token.isETH()) {
require( require(
msg.value == 0, msg.value == 0,
"User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction"
); );
IERC20(_token).safeTransferFrom(_user, feeAddress, _amount);
} else { } else {
require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); require(msg.value >= _amount, "The amount and the value sent to deposit do not match");
//solium-disable-next-line //solium-disable-next-line
(bool result, ) = feeAddress.call{ value: _amount, gas: 50000}(""); (bool result, ) = feeAddress.call{ value: _amount, gas: 50000}("");
require(result, "Transfer of ETH failed"); require(result, "Transfer of ETH failed");
} }
IERC20(_token).universalTransferFrom(
_user,
_feeAddress,
_amount,
false
);
} }
/** /**
@ -470,21 +466,14 @@ contract LendingPoolCore is VersionedInitializable {
function liquidateFee( function liquidateFee(
address _token, address _token,
uint256 _amount, uint256 _amount,
address _destination address _feeAddress
) external payable onlyLendingPool { ) external payable onlyLendingPool {
address payable feeAddress = address(uint160(_destination)); //cast the address to payable
require( require(
msg.value == 0, msg.value == 0,
"Fee liquidation does not require any transfer of value" "Fee liquidation does not require any transfer of value"
); );
if (_token != EthAddressLib.ethAddress()) { IERC20(_token).universalTransfer(_feeAddress, _amount);
IERC20(_token).safeTransfer(feeAddress, _amount);
} else {
//solium-disable-next-line
(bool result, ) = feeAddress.call{ value: _amount, gas: 50000}("");
require(result, "Transfer of ETH failed");
}
} }
/** /**
@ -498,21 +487,20 @@ contract LendingPoolCore is VersionedInitializable {
payable payable
onlyLendingPool onlyLendingPool
{ {
if (_reserve != EthAddressLib.ethAddress()) { IERC20 reserve = IERC20(_reserve);
require(msg.value == 0, "User is sending ETH along with the ERC20 transfer.");
IERC20(_reserve).safeTransferFrom(_user, address(this), _amount);
if (!reserve.isETH()) {
require(
msg.value == 0,
"User is sending ETH along with the ERC20 transfer."
);
} else { } else {
require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); require(
msg.value >= _amount,
if (msg.value > _amount) { "The amount and the value sent to deposit do not match"
//send back excess ETH );
uint256 excessAmount = msg.value.sub(_amount);
//solium-disable-next-line
(bool result, ) = _user.call{ value: _amount, gas: 50000}("");
require(result, "Transfer of ETH failed");
}
} }
reserve.universalTransferFrom(_user, address(this), _amount, true);
} }
/** /**
@ -616,14 +604,7 @@ contract LendingPoolCore is VersionedInitializable {
* @return the available liquidity * @return the available liquidity
**/ **/
function getReserveAvailableLiquidity(address _reserve) public view returns (uint256) { function getReserveAvailableLiquidity(address _reserve) public view returns (uint256) {
uint256 balance = 0; return IERC20(_reserve).universalBalanceOf(address(this));
if (_reserve == EthAddressLib.ethAddress()) {
balance = address(this).balance;
} else {
balance = IERC20(_reserve).balanceOf(address(this));
}
return balance;
} }
/** /**
@ -1443,7 +1424,6 @@ contract LendingPoolCore is VersionedInitializable {
//solium-disable-next-line //solium-disable-next-line
user.lastUpdateTimestamp = uint40(block.timestamp); user.lastUpdateTimestamp = uint40(block.timestamp);
} }
/** /**

View File

@ -0,0 +1,137 @@
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
library UniversalERC20 {
using SafeMath for uint256;
using SafeERC20 for IERC20;
IERC20 private constant ZERO_ADDRESS = IERC20(
0x0000000000000000000000000000000000000000
);
IERC20 private constant ETH_ADDRESS = IERC20(
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
);
function universalTransfer(
IERC20 token,
address to,
uint256 amount
) internal returns (bool) {
if (amount == 0) {
return true;
}
if (isETH(token)) {
(bool result, ) = payable(to).call{value: amount, gas: 50000}("");
require(result, "Transfer of ETH failed");
} else {
token.safeTransfer(to, amount);
}
return true;
}
function universalTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount,
bool returnExcess
) internal {
if (amount == 0) {
return;
}
if (isETH(token)) {
require(
msg.value >= amount,
"Wrong usage of ETH.universalTransferFrom()"
); // TODO: think one more time from == msg.sender
if (to != address(this)) {
(bool result, ) = payable(to).call{value: amount, gas: 50000}(
""
);
require(result, "Transfer of ETH failed");
}
if (returnExcess && msg.value > amount) {
(bool result, ) = msg.sender.call{
value: msg.value.sub(amount),
gas: 50000
}("");
require(result, "Transfer of ETH failed");
}
} else {
token.safeTransferFrom(from, to, amount);
}
}
function universalTransferFromSenderToThis(IERC20 token, uint256 amount)
internal
{
if (amount == 0) {
return;
}
if (isETH(token)) {
if (msg.value > amount) {
// Return remainder if exist
(bool result, ) = msg.sender.call{
value: msg.value.sub(amount),
gas: 50000
}("");
require(result, "Transfer of ETH failed");
}
} else {
token.safeTransferFrom(msg.sender, address(this), amount);
}
}
function universalApprove(
IERC20 token,
address to,
uint256 amount
) internal {
if (!isETH(token)) {
if (amount > 0 && token.allowance(address(this), to) > 0) {
token.safeApprove(to, 0);
}
token.safeApprove(to, amount);
}
}
function universalBalanceOf(IERC20 token, address who)
internal
view
returns (uint256)
{
if (isETH(token)) {
return who.balance;
} else {
return token.balanceOf(who);
}
}
function universalDecimals(IERC20 token) internal view returns (uint256) {
if (isETH(token)) {
return 18;
}
(bool success, bytes memory data) = address(token).staticcall{
gas: 10000
}(abi.encodeWithSignature("decimals()"));
if (!success || data.length == 0) {
(success, data) = address(token).staticcall{gas: 10000}(
abi.encodeWithSignature("DECIMALS()")
);
}
return (success && data.length > 0) ? abi.decode(data, (uint256)) : 18;
}
function isETH(IERC20 token) internal pure returns (bool) {
return (address(token) == address(ZERO_ADDRESS) ||
address(token) == address(ETH_ADDRESS));
}
}