From fea677a6076d67fd7b4a1dd7f70944a44ca99df2 Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 2 Jun 2020 16:49:24 +0300 Subject: [PATCH 01/12] migration to 1inch UniversalERC20 --- contracts/fees/MockKyberProxy.sol | 10 +- contracts/fees/MockOneSplit.sol | 4 +- contracts/fees/TokenDistributor.sol | 20 +-- .../flashloan/base/FlashLoanReceiverBase.sol | 22 +-- contracts/lendingpool/LendingPool.sol | 10 +- contracts/lendingpool/LendingPoolCore.sol | 72 ++++----- contracts/libraries/UniversalERC20.sol | 137 ++++++++++++++++++ 7 files changed, 188 insertions(+), 87 deletions(-) create mode 100644 contracts/libraries/UniversalERC20.sol diff --git a/contracts/fees/MockKyberProxy.sol b/contracts/fees/MockKyberProxy.sol index 3f8e4629..354b84a3 100644 --- a/contracts/fees/MockKyberProxy.sol +++ b/contracts/fees/MockKyberProxy.sol @@ -5,6 +5,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../libraries/EthAddressLib.sol"; +import "../libraries/UniversalERC20.sol"; import "../mocks/tokens/MintableERC20.sol"; /// @title MockKyberProxy @@ -16,6 +17,7 @@ import "../mocks/tokens/MintableERC20.sol"; contract MockKyberProxy { using SafeERC20 for IERC20; using SafeERC20 for MintableERC20; + using UniversalERC20 for IERC20; /// @notice The token which the msg.sender of tradeWithHint will burn MintableERC20 public tokenToBurn; @@ -31,15 +33,15 @@ contract MockKyberProxy { IERC20 _toToken, address _receiver, uint256 _maxAmount, - uint minConversionRate, + uint256 minConversionRate, address _referral, bytes calldata _filtering - ) external payable returns(uint256) { + ) external payable returns (uint256) { 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); } tokenToBurn.safeTransfer(msg.sender, 1 ether); return 1 ether; } -} \ No newline at end of file +} diff --git a/contracts/fees/MockOneSplit.sol b/contracts/fees/MockOneSplit.sol index 51e4a02b..f6e29d68 100644 --- a/contracts/fees/MockOneSplit.sol +++ b/contracts/fees/MockOneSplit.sol @@ -8,10 +8,12 @@ import "../libraries/EthAddressLib.sol"; import "../mocks/tokens/MintableERC20.sol"; import "../interfaces/IOneSplit.sol"; +import "../libraries/UniversalERC20.sol"; contract MockOneSplit is IOneSplit { using SafeERC20 for IERC20; using SafeERC20 for MintableERC20; + using UniversalERC20 for IERC20; MintableERC20 public tokenToBurn; @@ -55,7 +57,7 @@ contract MockOneSplit is IOneSplit { uint256 disableFlags ) public override payable { 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); } tokenToBurn.safeTransfer(msg.sender, 10000 ether); diff --git a/contracts/fees/TokenDistributor.sol b/contracts/fees/TokenDistributor.sol index 0c6f6bbd..3db76462 100644 --- a/contracts/fees/TokenDistributor.sol +++ b/contracts/fees/TokenDistributor.sol @@ -11,6 +11,7 @@ import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol"; import "../interfaces/IKyberNetworkProxyInterface.sol"; import "../interfaces/IExchangeAdapter.sol"; import "../libraries/EthAddressLib.sol"; +import "../libraries/UniversalERC20.sol"; /// @title TokenDistributor @@ -26,6 +27,7 @@ import "../libraries/EthAddressLib.sol"; contract TokenDistributor is ReentrancyGuard, VersionedInitializable { using SafeMath for uint256; using SafeERC20 for IERC20; + using UniversalERC20 for IERC20; struct Distribution { address[] receivers; @@ -104,9 +106,8 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable { /// @param _tokens list of ERC20 tokens to distribute function distribute(IERC20[] memory _tokens) public { for (uint256 i = 0; i < _tokens.length; i++) { - uint256 _balanceToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress()) - ? _tokens[i].balanceOf(address(this)) - : address(this).balance; + uint256 _balanceToDistribute = _tokens[i].universalBalanceOf(address(this)); + if (_balanceToDistribute <= 0) { continue; } @@ -129,9 +130,8 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable { /// @param _percentages list of percentages to distribute per token function distributeWithPercentages(IERC20[] memory _tokens, uint256[] memory _percentages) public { for (uint256 i = 0; i < _tokens.length; i++) { - uint256 _amountToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress()) - ? _tokens[i].balanceOf(address(this)).mul(_percentages[i]).div(100) - : address(this).balance.mul(_percentages[i]).div(100); + uint256 _amountToDistribute = _tokens[i].universalBalanceOf(address(this)).mul(_percentages[i]).div(100); + if (_amountToDistribute <= 0) { continue; } @@ -166,13 +166,7 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable { } if (_distribution.receivers[j] != address(0)) { - if (_tokenAddress != EthAddressLib.ethAddress()) { - _token.safeTransfer(_distribution.receivers[j], _amount); - } else { - //solium-disable-next-line - (bool _success,) = _distribution.receivers[j].call{value: _amount}(""); - require(_success, "Reverted ETH transfer"); - } + _token.universalTransfer(_distribution.receivers[j], _amount); emit Distributed(_distribution.receivers[j], _distribution.percentages[j], _amount); } else { uint256 _amountToBurn = _amount; diff --git a/contracts/flashloan/base/FlashLoanReceiverBase.sol b/contracts/flashloan/base/FlashLoanReceiverBase.sol index b94b8976..19bc847a 100644 --- a/contracts/flashloan/base/FlashLoanReceiverBase.sol +++ b/contracts/flashloan/base/FlashLoanReceiverBase.sol @@ -7,10 +7,12 @@ import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../interfaces/IFlashLoanReceiver.sol"; import "../../interfaces/ILendingPoolAddressesProvider.sol"; import "../../libraries/EthAddressLib.sol"; +import "../../libraries/UniversalERC20.sol"; abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { using SafeERC20 for IERC20; + using UniversalERC20 for IERC20; using SafeMath for uint256; ILendingPoolAddressesProvider public addressesProvider; @@ -25,28 +27,14 @@ abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { address payable core = addressesProvider.getLendingPoolCore(); - transferInternal(core,_reserve, _amount); + transferInternal(core, _reserve, _amount); } function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal { - if(_reserve == EthAddressLib.ethAddress()) { - //solium-disable-next-line - _destination.call{value: _amount}(""); - return; - } - - IERC20(_reserve).safeTransfer(_destination, _amount); - - + IERC20(_reserve).universalTransfer(_destination, _amount); } function getBalanceInternal(address _target, address _reserve) internal view returns(uint256) { - if(_reserve == EthAddressLib.ethAddress()) { - - return _target.balance; - } - - return IERC20(_reserve).balanceOf(_target); - + return IERC20(_reserve).universalBalanceOf(_target); } } \ No newline at end of file diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index dc5ce58a..1c0b7bb6 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -18,6 +18,7 @@ import "./LendingPoolCore.sol"; import "./LendingPoolDataProvider.sol"; import "./LendingPoolLiquidationManager.sol"; import "../libraries/EthAddressLib.sol"; +import "../libraries/UniversalERC20.sol"; /** * @title LendingPool contract @@ -29,6 +30,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { using SafeMath for uint256; using WadRayMath for uint256; using Address for address; + using UniversalERC20 for IERC20; LendingPoolAddressesProvider public addressesProvider; LendingPoolCore public core; @@ -849,9 +851,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { { //check that the reserve has enough available liquidity //we avoid using the getAvailableLiquidity() function in LendingPoolCore to save gas - uint256 availableLiquidityBefore = _reserve == EthAddressLib.ethAddress() - ? address(core).balance - : IERC20(_reserve).balanceOf(address(core)); + uint256 availableLiquidityBefore = IERC20(_reserve).universalBalanceOf(address(core)); require( availableLiquidityBefore >= _amount, @@ -882,9 +882,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { receiver.executeOperation(_reserve, _amount, amountFee, _params); //check that the actual balance of the core contract includes the returned amount - uint256 availableLiquidityAfter = _reserve == EthAddressLib.ethAddress() - ? address(core).balance - : IERC20(_reserve).balanceOf(address(core)); + uint256 availableLiquidityAfter = IERC20(_reserve).universalBalanceOf(address(core)); require( availableLiquidityAfter == availableLiquidityBefore.add(amountFee), diff --git a/contracts/lendingpool/LendingPoolCore.sol b/contracts/lendingpool/LendingPoolCore.sol index 7a65e788..8831fa7e 100644 --- a/contracts/lendingpool/LendingPoolCore.sol +++ b/contracts/lendingpool/LendingPoolCore.sol @@ -2,7 +2,6 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol"; @@ -14,6 +13,7 @@ import "../interfaces/IReserveInterestRateStrategy.sol"; import "../libraries/WadRayMath.sol"; import "../tokenization/AToken.sol"; import "../libraries/EthAddressLib.sol"; +import "../libraries/UniversalERC20.sol"; /** * @title LendingPoolCore contract @@ -29,10 +29,9 @@ contract LendingPoolCore is VersionedInitializable { using WadRayMath for uint256; using CoreLibrary for CoreLibrary.ReserveData; using CoreLibrary for CoreLibrary.UserReserveData; - using SafeERC20 for IERC20; + using UniversalERC20 for IERC20; using Address for address payable; - /** * @dev DEPRECATED: This event was used in previous LendingPoolCore implementations, and it has been replaced by ReserveDataUpdated() * @param reserve the address of the reserve @@ -51,7 +50,6 @@ contract LendingPoolCore is VersionedInitializable { uint256 variableBorrowIndex ); - /** * @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 @@ -362,7 +360,6 @@ contract LendingPoolCore is VersionedInitializable { _collateralToLiquidate.add(_liquidatedCollateralForFee) ); } - } /** @@ -422,13 +419,7 @@ contract LendingPoolCore is VersionedInitializable { external onlyLendingPool { - if (_reserve != EthAddressLib.ethAddress()) { - IERC20(_reserve).safeTransfer(_user, _amount); - } else { - //solium-disable-next-line - (bool result, ) = _user.call{value: _amount, gas: 50000}(""); - require(result, "Transfer of ETH failed"); - } + IERC20(_reserve).universalTransfer(_user, _amount); } /** @@ -443,22 +434,27 @@ contract LendingPoolCore is VersionedInitializable { address _token, address _user, uint256 _amount, - address _destination + address _feeAddress ) 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( msg.value == 0, "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" ); - IERC20(_token).safeTransferFrom(_user, feeAddress, _amount); } else { require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); //solium-disable-next-line (bool result, ) = feeAddress.call{ value: _amount, gas: 50000}(""); require(result, "Transfer of ETH failed"); } + IERC20(_token).universalTransferFrom( + _user, + _feeAddress, + _amount, + false + ); } /** @@ -470,21 +466,14 @@ contract LendingPoolCore is VersionedInitializable { function liquidateFee( address _token, uint256 _amount, - address _destination + address _feeAddress ) external payable onlyLendingPool { - address payable feeAddress = address(uint160(_destination)); //cast the address to payable require( msg.value == 0, "Fee liquidation does not require any transfer of value" ); - if (_token != EthAddressLib.ethAddress()) { - IERC20(_token).safeTransfer(feeAddress, _amount); - } else { - //solium-disable-next-line - (bool result, ) = feeAddress.call{ value: _amount, gas: 50000}(""); - require(result, "Transfer of ETH failed"); - } + IERC20(_token).universalTransfer(_feeAddress, _amount); } /** @@ -498,21 +487,20 @@ contract LendingPoolCore is VersionedInitializable { payable onlyLendingPool { - if (_reserve != EthAddressLib.ethAddress()) { - require(msg.value == 0, "User is sending ETH along with the ERC20 transfer."); - IERC20(_reserve).safeTransferFrom(_user, address(this), _amount); + IERC20 reserve = IERC20(_reserve); + if (!reserve.isETH()) { + require( + msg.value == 0, + "User is sending ETH along with the ERC20 transfer." + ); } else { - require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); - - if (msg.value > _amount) { - //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"); - } + require( + msg.value >= _amount, + "The amount and the value sent to deposit do not match" + ); } + reserve.universalTransferFrom(_user, address(this), _amount, true); } /** @@ -616,14 +604,7 @@ contract LendingPoolCore is VersionedInitializable { * @return the available liquidity **/ function getReserveAvailableLiquidity(address _reserve) public view returns (uint256) { - uint256 balance = 0; - - if (_reserve == EthAddressLib.ethAddress()) { - balance = address(this).balance; - } else { - balance = IERC20(_reserve).balanceOf(address(this)); - } - return balance; + return IERC20(_reserve).universalBalanceOf(address(this)); } /** @@ -1443,7 +1424,6 @@ contract LendingPoolCore is VersionedInitializable { //solium-disable-next-line user.lastUpdateTimestamp = uint40(block.timestamp); - } /** diff --git a/contracts/libraries/UniversalERC20.sol b/contracts/libraries/UniversalERC20.sol new file mode 100644 index 00000000..1cfc822b --- /dev/null +++ b/contracts/libraries/UniversalERC20.sol @@ -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)); + } +} From 2dfcc970dac1487845bfaec929afd50ee4a98a6b Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 2 Jun 2020 16:54:46 +0300 Subject: [PATCH 02/12] fix some typo --- contracts/lendingpool/LendingPoolCore.sol | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/contracts/lendingpool/LendingPoolCore.sol b/contracts/lendingpool/LendingPoolCore.sol index 8831fa7e..a685b2cb 100644 --- a/contracts/lendingpool/LendingPoolCore.sol +++ b/contracts/lendingpool/LendingPoolCore.sol @@ -427,7 +427,7 @@ contract LendingPoolCore is VersionedInitializable { * @param _token the address of the token being transferred * @param _user the address of the user from where the transfer is happening * @param _amount the amount being transferred - * @param _destination the fee receiver address + * @param _feeAddress the fee receiver address **/ function transferToFeeCollectionAddress( @@ -446,7 +446,7 @@ contract LendingPoolCore is VersionedInitializable { } else { require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); //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"); } IERC20(_token).universalTransferFrom( @@ -461,7 +461,7 @@ contract LendingPoolCore is VersionedInitializable { * @dev transfers the fees to the fees collection address in the case of liquidation * @param _token the address of the token being transferred * @param _amount the amount being transferred - * @param _destination the fee receiver address + * @param _feeAddress the fee receiver address **/ function liquidateFee( address _token, @@ -1749,15 +1749,10 @@ contract LendingPoolCore is VersionedInitializable { **/ function transferFlashLoanProtocolFeeInternal(address _token, uint256 _amount) internal { - address payable receiver = payable(addressesProvider.getTokenDistributor()); - - if (_token != EthAddressLib.ethAddress()) { - IERC20(_token).safeTransfer(receiver, _amount); - } else { - //solium-disable-next-line - (bool result, ) = receiver.call{ value: _amount }(""); - require(result, "Transfer to token distributor failed"); - } + IERC20(_token).universalTransfer( + addressesProvider.getTokenDistributor(), + _amount + ); } /** From f3597f670fb56902d552b075c7a23a4fe9cbfefd Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 2 Jun 2020 17:16:22 +0300 Subject: [PATCH 03/12] remove EthAddressLib --- contracts/fees/MockKyberProxy.sol | 1 - contracts/fees/MockOneSplit.sol | 3 +-- contracts/fees/OneSplitAdapter.sol | 11 ++++++----- contracts/fees/TokenDistributor.sol | 3 +-- contracts/flashloan/base/FlashLoanReceiverBase.sol | 3 +-- contracts/interfaces/IExchangeAdapter.sol | 3 +-- contracts/lendingpool/LendingPool.sol | 3 +-- contracts/lendingpool/LendingPoolCore.sol | 1 - contracts/libraries/EthAddressLib.sol | 13 ------------- contracts/misc/ChainlinkProxyPriceProvider.sol | 9 ++++++--- contracts/misc/WalletBalanceProvider.sol | 9 +++++---- contracts/mocks/flashloan/MockFlashLoanReceiver.sol | 8 ++++++-- 12 files changed, 28 insertions(+), 39 deletions(-) delete mode 100644 contracts/libraries/EthAddressLib.sol diff --git a/contracts/fees/MockKyberProxy.sol b/contracts/fees/MockKyberProxy.sol index 354b84a3..8f812fe3 100644 --- a/contracts/fees/MockKyberProxy.sol +++ b/contracts/fees/MockKyberProxy.sol @@ -4,7 +4,6 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "../libraries/EthAddressLib.sol"; import "../libraries/UniversalERC20.sol"; import "../mocks/tokens/MintableERC20.sol"; diff --git a/contracts/fees/MockOneSplit.sol b/contracts/fees/MockOneSplit.sol index f6e29d68..60668c4b 100644 --- a/contracts/fees/MockOneSplit.sol +++ b/contracts/fees/MockOneSplit.sol @@ -4,7 +4,6 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "../libraries/EthAddressLib.sol"; import "../mocks/tokens/MintableERC20.sol"; import "../interfaces/IOneSplit.sol"; @@ -62,4 +61,4 @@ contract MockOneSplit is IOneSplit { } tokenToBurn.safeTransfer(msg.sender, 10000 ether); } -} \ No newline at end of file +} diff --git a/contracts/fees/OneSplitAdapter.sol b/contracts/fees/OneSplitAdapter.sol index b4773ac3..a77bf3ef 100644 --- a/contracts/fees/OneSplitAdapter.sol +++ b/contracts/fees/OneSplitAdapter.sol @@ -7,7 +7,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IOneSplit.sol"; import "../interfaces/IPriceOracleGetter.sol"; import "../interfaces/IExchangeAdapter.sol"; - +import "../libraries/UniversalERC20.sol"; /// @title OneSplitAdapter /// @author Aave /// @notice Implements the logic to exchange assets through 1Split @@ -26,6 +26,7 @@ import "../interfaces/IExchangeAdapter.sol"; contract OneSplitAdapter is IExchangeAdapter { using SafeMath for uint256; + using UniversalERC20 for IERC20; event OneSplitAdapterSetup(address oneSplit, address priceOracle, uint256 splitParts); @@ -37,20 +38,20 @@ contract OneSplitAdapter is IExchangeAdapter { /// @param _tokens the list of token addresses to approve function approveExchange(IERC20[] calldata _tokens) external override { for (uint256 i = 0; i < _tokens.length; i++) { - if (address(_tokens[i]) != EthAddressLib.ethAddress()) { + if (!_tokens[i].isETH()) { _tokens[i].safeApprove(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, UintConstants.maxUintMinus1()); } } } /// @notice Exchanges _amount of _from token (or ETH) to _to token (or ETH) - /// - Uses EthAddressLib.ethAddress() as the reference on 1Split of ETH + /// - Uses UniversalERC20.isETH() as the reference on 1Split of ETH /// @param _from The asset to exchange from /// @param _to The asset to exchange to /// @param _amount The amount to exchange /// @param _maxSlippage Max slippage acceptable, taken into account after the goodSwap() function exchange(address _from, address _to, uint256 _amount, uint256 _maxSlippage) external override returns(uint256) { - uint256 _value = (_from == EthAddressLib.ethAddress()) ? _amount : 0; + uint256 _value = IERC20(_from).isETH() ? _amount : 0; uint256 _fromAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_from); uint256 _toAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_to); @@ -76,4 +77,4 @@ contract OneSplitAdapter is IExchangeAdapter { emit Exchange(_from, _to, 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, _amount, _toReceivedAmount); return _toReceivedAmount; } -} \ No newline at end of file +} diff --git a/contracts/fees/TokenDistributor.sol b/contracts/fees/TokenDistributor.sol index 3db76462..a282ed27 100644 --- a/contracts/fees/TokenDistributor.sol +++ b/contracts/fees/TokenDistributor.sol @@ -10,7 +10,6 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol"; import "../interfaces/IKyberNetworkProxyInterface.sol"; import "../interfaces/IExchangeAdapter.sol"; -import "../libraries/EthAddressLib.sol"; import "../libraries/UniversalERC20.sol"; @@ -209,4 +208,4 @@ contract TokenDistributor is ReentrancyGuard, VersionedInitializable { return IMPLEMENTATION_REVISION; } -} \ No newline at end of file +} diff --git a/contracts/flashloan/base/FlashLoanReceiverBase.sol b/contracts/flashloan/base/FlashLoanReceiverBase.sol index 19bc847a..25fa03c0 100644 --- a/contracts/flashloan/base/FlashLoanReceiverBase.sol +++ b/contracts/flashloan/base/FlashLoanReceiverBase.sol @@ -6,7 +6,6 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../interfaces/IFlashLoanReceiver.sol"; import "../../interfaces/ILendingPoolAddressesProvider.sol"; -import "../../libraries/EthAddressLib.sol"; import "../../libraries/UniversalERC20.sol"; abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { @@ -37,4 +36,4 @@ abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { function getBalanceInternal(address _target, address _reserve) internal view returns(uint256) { return IERC20(_reserve).universalBalanceOf(_target); } -} \ No newline at end of file +} diff --git a/contracts/interfaces/IExchangeAdapter.sol b/contracts/interfaces/IExchangeAdapter.sol index 54343d58..1267f2b9 100644 --- a/contracts/interfaces/IExchangeAdapter.sol +++ b/contracts/interfaces/IExchangeAdapter.sol @@ -4,7 +4,6 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "../libraries/EthAddressLib.sol"; import "../libraries/UintConstants.sol"; interface IExchangeAdapter { @@ -21,4 +20,4 @@ interface IExchangeAdapter { function approveExchange(IERC20[] calldata _tokens) external; function exchange(address _from, address _to, uint256 _amount, uint256 _maxSlippage) external returns(uint256); -} \ No newline at end of file +} diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 1c0b7bb6..e8d0b337 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -17,7 +17,6 @@ import "../flashloan/interfaces/IFlashLoanReceiver.sol"; import "./LendingPoolCore.sol"; import "./LendingPoolDataProvider.sol"; import "./LendingPoolLiquidationManager.sol"; -import "../libraries/EthAddressLib.sol"; import "../libraries/UniversalERC20.sol"; /** @@ -550,7 +549,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { ) = core.getUserBorrowBalances(_reserve, _onBehalfOf); vars.originationFee = core.getUserOriginationFee(_reserve, _onBehalfOf); - vars.isETH = EthAddressLib.ethAddress() == _reserve; + vars.isETH = IERC20(_reserve).isETH(); require(vars.compoundedBorrowBalance > 0, "The user does not have any borrow pending"); diff --git a/contracts/lendingpool/LendingPoolCore.sol b/contracts/lendingpool/LendingPoolCore.sol index a685b2cb..4606dfdd 100644 --- a/contracts/lendingpool/LendingPoolCore.sol +++ b/contracts/lendingpool/LendingPoolCore.sol @@ -12,7 +12,6 @@ import "../interfaces/ILendingRateOracle.sol"; import "../interfaces/IReserveInterestRateStrategy.sol"; import "../libraries/WadRayMath.sol"; import "../tokenization/AToken.sol"; -import "../libraries/EthAddressLib.sol"; import "../libraries/UniversalERC20.sol"; /** diff --git a/contracts/libraries/EthAddressLib.sol b/contracts/libraries/EthAddressLib.sol deleted file mode 100644 index 07d485f9..00000000 --- a/contracts/libraries/EthAddressLib.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.8; - -library EthAddressLib { - - /** - * @dev returns the address used within the protocol to identify ETH - * @return the address assigned to ETH - */ - function ethAddress() internal pure returns(address) { - return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - } -} \ No newline at end of file diff --git a/contracts/misc/ChainlinkProxyPriceProvider.sol b/contracts/misc/ChainlinkProxyPriceProvider.sol index e2c6d8fa..3695496c 100644 --- a/contracts/misc/ChainlinkProxyPriceProvider.sol +++ b/contracts/misc/ChainlinkProxyPriceProvider.sol @@ -2,10 +2,12 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + import "../interfaces/IPriceOracleGetter.sol"; import "../interfaces/IChainlinkAggregator.sol"; -import "../libraries/EthAddressLib.sol"; +import "../libraries/UniversalERC20.sol"; /// @title ChainlinkProxyPriceProvider /// @author Aave @@ -15,6 +17,7 @@ import "../libraries/EthAddressLib.sol"; /// - Owned by the Aave governance system, allowed to add sources for assets, replace them /// and change the fallbackOracle contract ChainlinkProxyPriceProvider is IPriceOracleGetter, Ownable { + using UniversalERC20 for IERC20; event AssetSourceUpdated(address indexed asset, address indexed source); event FallbackOracleUpdated(address indexed fallbackOracle); @@ -68,7 +71,7 @@ contract ChainlinkProxyPriceProvider is IPriceOracleGetter, Ownable { /// @param _asset The asset address function getAssetPrice(address _asset) public override view returns(uint256) { IChainlinkAggregator source = assetsSources[_asset]; - if (_asset == EthAddressLib.ethAddress()) { + if (IERC20(_asset).isETH()) { return 1 ether; } else { // If there is no registered source for the asset, call the fallbackOracle @@ -107,4 +110,4 @@ contract ChainlinkProxyPriceProvider is IPriceOracleGetter, Ownable { function getFallbackOracle() external view returns(address) { return address(fallbackOracle); } -} \ No newline at end of file +} diff --git a/contracts/misc/WalletBalanceProvider.sol b/contracts/misc/WalletBalanceProvider.sol index 36c28bb4..73b8624e 100644 --- a/contracts/misc/WalletBalanceProvider.sol +++ b/contracts/misc/WalletBalanceProvider.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../configuration/LendingPoolAddressesProvider.sol"; import "../lendingpool/LendingPoolCore.sol"; -import "../libraries/EthAddressLib.sol"; +import "../libraries/UniversalERC20.sol"; /** @@ -19,6 +19,7 @@ import "../libraries/EthAddressLib.sol"; contract WalletBalanceProvider { using Address for address; + using UniversalERC20 for IERC20; LendingPoolAddressesProvider provider; @@ -59,7 +60,7 @@ contract WalletBalanceProvider { for (uint256 i = 0; i < _users.length; i++) { for (uint256 j = 0; j < _tokens.length; j++) { uint256 _offset = i * _tokens.length; - if (_tokens[j] == EthAddressLib.ethAddress()) { + if (IERC20(_tokens[j]).isETH()) { balances[_offset + j] = _users[i].balance; // ETH balance } else { if (!_tokens[j].isContract()) { @@ -91,7 +92,7 @@ contract WalletBalanceProvider { balances[j] = 0; continue; } - if (reserves[j] != EthAddressLib.ethAddress()) { + if (!IERC20(reserves[j]).isETH()) { balances[j] = balanceOf(_user, reserves[j]); } else { balances[j] = _user.balance; // ETH balance @@ -100,4 +101,4 @@ contract WalletBalanceProvider { return (reserves, balances); } -} \ No newline at end of file +} diff --git a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol index 688d507f..932e630d 100644 --- a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol +++ b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol @@ -2,13 +2,17 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../../flashloan/base/FlashLoanReceiverBase.sol"; import "../tokens/MintableERC20.sol"; +import "../libraries/UniversalERC20.sol"; contract MockFlashLoanReceiver is FlashLoanReceiverBase { using SafeMath for uint256; + using UniversalERC20 for IERC20; + event ExecutedWithFail(address _reserve, uint256 _amount, uint256 _fee); event ExecutedWithSuccess(address _reserve, uint256 _amount, uint256 _fee); @@ -42,11 +46,11 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase { //execution does not fail - mint tokens and return them to the _destination //note: if the reserve is eth, the mock contract must receive at least _fee ETH before calling executeOperation - if(_reserve != EthAddressLib.ethAddress()) { + if(!IERC20(_reserve).isETH()) { token.mint(_fee); } //returning amount + fee to the destination transferFundsBackToPoolInternal(_reserve, _amount.add(_fee)); emit ExecutedWithSuccess(_reserve, _amount, _fee); } -} \ No newline at end of file +} From 268e749a6d1dfec2f4202cf35e4701db22093bf2 Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 2 Jun 2020 18:16:27 +0300 Subject: [PATCH 04/12] remove SafeERC20 dependency --- contracts/fees/MockKyberProxy.sol | 8 +++----- contracts/fees/MockOneSplit.sol | 8 +++----- contracts/fees/OneSplitAdapter.sol | 2 +- contracts/fees/TokenDistributor.sol | 2 -- contracts/flashloan/base/FlashLoanReceiverBase.sol | 2 -- contracts/interfaces/IExchangeAdapter.sol | 3 --- contracts/lendingpool/LendingPoolCore.sol | 3 --- contracts/libraries/UniversalERC20.sol | 8 ++++---- contracts/mocks/flashloan/MockFlashLoanReceiver.sol | 2 +- 9 files changed, 12 insertions(+), 26 deletions(-) diff --git a/contracts/fees/MockKyberProxy.sol b/contracts/fees/MockKyberProxy.sol index 8f812fe3..044c4b20 100644 --- a/contracts/fees/MockKyberProxy.sol +++ b/contracts/fees/MockKyberProxy.sol @@ -2,7 +2,6 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../libraries/UniversalERC20.sol"; import "../mocks/tokens/MintableERC20.sol"; @@ -14,9 +13,8 @@ import "../mocks/tokens/MintableERC20.sol"; /// - Mints the tokenToBurn /// - Sends back the tokenToBurn contract MockKyberProxy { - using SafeERC20 for IERC20; - using SafeERC20 for MintableERC20; using UniversalERC20 for IERC20; + using UniversalERC20 for MintableERC20; /// @notice The token which the msg.sender of tradeWithHint will burn MintableERC20 public tokenToBurn; @@ -38,9 +36,9 @@ contract MockKyberProxy { ) external payable returns (uint256) { require(tokenToBurn.mint(1 ether), "TRADE_WITH_HINT. Reverted mint()"); if (!_fromToken.isETH()) { - _fromToken.safeTransferFrom(msg.sender, address(this), _amount); + _fromToken.universalTransferFrom(msg.sender, address(this), _amount, false); } - tokenToBurn.safeTransfer(msg.sender, 1 ether); + tokenToBurn.universalTransfer(msg.sender, 1 ether); return 1 ether; } } diff --git a/contracts/fees/MockOneSplit.sol b/contracts/fees/MockOneSplit.sol index 60668c4b..96d8fc74 100644 --- a/contracts/fees/MockOneSplit.sol +++ b/contracts/fees/MockOneSplit.sol @@ -2,7 +2,6 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../mocks/tokens/MintableERC20.sol"; @@ -10,8 +9,7 @@ import "../interfaces/IOneSplit.sol"; import "../libraries/UniversalERC20.sol"; contract MockOneSplit is IOneSplit { - using SafeERC20 for IERC20; - using SafeERC20 for MintableERC20; + using UniversalERC20 for MintableERC20; using UniversalERC20 for IERC20; MintableERC20 public tokenToBurn; @@ -57,8 +55,8 @@ contract MockOneSplit is IOneSplit { ) public override payable { require(tokenToBurn.mint(10000 ether), "TRADE_WITH_HINT. Reverted mint()"); if (!fromToken.isETH()) { - fromToken.safeTransferFrom(msg.sender, address(this), amount); + fromToken.universalTransferFrom(msg.sender, address(this), amount, false); } - tokenToBurn.safeTransfer(msg.sender, 10000 ether); + tokenToBurn.universalTransfer(msg.sender, 10000 ether); } } diff --git a/contracts/fees/OneSplitAdapter.sol b/contracts/fees/OneSplitAdapter.sol index a77bf3ef..9d87c6ef 100644 --- a/contracts/fees/OneSplitAdapter.sol +++ b/contracts/fees/OneSplitAdapter.sol @@ -39,7 +39,7 @@ contract OneSplitAdapter is IExchangeAdapter { function approveExchange(IERC20[] calldata _tokens) external override { for (uint256 i = 0; i < _tokens.length; i++) { if (!_tokens[i].isETH()) { - _tokens[i].safeApprove(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, UintConstants.maxUintMinus1()); + _tokens[i].universalApprove(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, UintConstants.maxUintMinus1()); } } } diff --git a/contracts/fees/TokenDistributor.sol b/contracts/fees/TokenDistributor.sol index a282ed27..3368e4e6 100644 --- a/contracts/fees/TokenDistributor.sol +++ b/contracts/fees/TokenDistributor.sol @@ -3,7 +3,6 @@ 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"; import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; @@ -25,7 +24,6 @@ import "../libraries/UniversalERC20.sol"; /// and burn it (sending to address(0) the tokenToBurn) contract TokenDistributor is ReentrancyGuard, VersionedInitializable { using SafeMath for uint256; - using SafeERC20 for IERC20; using UniversalERC20 for IERC20; struct Distribution { diff --git a/contracts/flashloan/base/FlashLoanReceiverBase.sol b/contracts/flashloan/base/FlashLoanReceiverBase.sol index 25fa03c0..fb865030 100644 --- a/contracts/flashloan/base/FlashLoanReceiverBase.sol +++ b/contracts/flashloan/base/FlashLoanReceiverBase.sol @@ -3,14 +3,12 @@ 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"; import "../interfaces/IFlashLoanReceiver.sol"; import "../../interfaces/ILendingPoolAddressesProvider.sol"; import "../../libraries/UniversalERC20.sol"; abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { - using SafeERC20 for IERC20; using UniversalERC20 for IERC20; using SafeMath for uint256; diff --git a/contracts/interfaces/IExchangeAdapter.sol b/contracts/interfaces/IExchangeAdapter.sol index 1267f2b9..b5bd8fc3 100644 --- a/contracts/interfaces/IExchangeAdapter.sol +++ b/contracts/interfaces/IExchangeAdapter.sol @@ -2,13 +2,10 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../libraries/UintConstants.sol"; interface IExchangeAdapter { - using SafeERC20 for IERC20; - event Exchange( address indexed from, address indexed to, diff --git a/contracts/lendingpool/LendingPoolCore.sol b/contracts/lendingpool/LendingPoolCore.sol index 4606dfdd..c99c9743 100644 --- a/contracts/lendingpool/LendingPoolCore.sol +++ b/contracts/lendingpool/LendingPoolCore.sol @@ -444,9 +444,6 @@ contract LendingPoolCore is VersionedInitializable { ); } else { require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); - //solium-disable-next-line - (bool result, ) = _feeAddress.call{ value: _amount, gas: 50000}(""); - require(result, "Transfer of ETH failed"); } IERC20(_token).universalTransferFrom( _user, diff --git a/contracts/libraries/UniversalERC20.sol b/contracts/libraries/UniversalERC20.sol index 1cfc822b..7feccd10 100644 --- a/contracts/libraries/UniversalERC20.sol +++ b/contracts/libraries/UniversalERC20.sol @@ -26,7 +26,7 @@ library UniversalERC20 { if (isETH(token)) { (bool result, ) = payable(to).call{value: amount, gas: 50000}(""); - require(result, "Transfer of ETH failed"); + require(result, "ETH_TRANSFER_FAILED"); } else { token.safeTransfer(to, amount); } @@ -53,14 +53,14 @@ library UniversalERC20 { (bool result, ) = payable(to).call{value: amount, gas: 50000}( "" ); - require(result, "Transfer of ETH failed"); + require(result, "ETH_TRANSFER_FAILED"); } if (returnExcess && msg.value > amount) { (bool result, ) = msg.sender.call{ value: msg.value.sub(amount), gas: 50000 }(""); - require(result, "Transfer of ETH failed"); + require(result, "ETH_TRANSFER_FAILED"); } } else { token.safeTransferFrom(from, to, amount); @@ -81,7 +81,7 @@ library UniversalERC20 { value: msg.value.sub(amount), gas: 50000 }(""); - require(result, "Transfer of ETH failed"); + require(result, "ETH_TRANSFER_FAILED"); } } else { token.safeTransferFrom(msg.sender, address(this), amount); diff --git a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol index 932e630d..6b80ea1a 100644 --- a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol +++ b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../../flashloan/base/FlashLoanReceiverBase.sol"; import "../tokens/MintableERC20.sol"; -import "../libraries/UniversalERC20.sol"; +import "../../libraries/UniversalERC20.sol"; contract MockFlashLoanReceiver is FlashLoanReceiverBase { From e31d5b9bee4c2de7541d96692a85db10b4c0b36c Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 3 Jun 2020 14:02:18 +0300 Subject: [PATCH 05/12] add doc strings to UniversalERC20, delete remove values there, fixes from MR comments --- contracts/fees/OneSplitAdapter.sol | 7 +-- .../flashloan/base/FlashLoanReceiverBase.sol | 16 ++---- contracts/libraries/UniversalERC20.sol | 54 +++++++++++++++++-- .../mocks/flashloan/MockFlashLoanReceiver.sol | 5 +- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/contracts/fees/OneSplitAdapter.sol b/contracts/fees/OneSplitAdapter.sol index 9d87c6ef..2b7363c0 100644 --- a/contracts/fees/OneSplitAdapter.sol +++ b/contracts/fees/OneSplitAdapter.sol @@ -38,9 +38,10 @@ contract OneSplitAdapter is IExchangeAdapter { /// @param _tokens the list of token addresses to approve function approveExchange(IERC20[] calldata _tokens) external override { for (uint256 i = 0; i < _tokens.length; i++) { - if (!_tokens[i].isETH()) { - _tokens[i].universalApprove(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, UintConstants.maxUintMinus1()); - } + _tokens[i].universalApprove( + 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, + UintConstants.maxUintMinus1() + ); } } diff --git a/contracts/flashloan/base/FlashLoanReceiverBase.sol b/contracts/flashloan/base/FlashLoanReceiverBase.sol index fb865030..95ce9229 100644 --- a/contracts/flashloan/base/FlashLoanReceiverBase.sol +++ b/contracts/flashloan/base/FlashLoanReceiverBase.sol @@ -21,17 +21,9 @@ abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { receive() external payable {} function transferFundsBackToPoolInternal(address _reserve, uint256 _amount) internal { - - address payable core = addressesProvider.getLendingPoolCore(); - - transferInternal(core, _reserve, _amount); - } - - function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal { - IERC20(_reserve).universalTransfer(_destination, _amount); - } - - function getBalanceInternal(address _target, address _reserve) internal view returns(uint256) { - return IERC20(_reserve).universalBalanceOf(_target); + IERC20(_reserve).universalTransfer( + addressesProvider.getLendingPoolCore(), // lending-pool core address + _amount + ); } } diff --git a/contracts/libraries/UniversalERC20.sol b/contracts/libraries/UniversalERC20.sol index 7feccd10..89283b9c 100644 --- a/contracts/libraries/UniversalERC20.sol +++ b/contracts/libraries/UniversalERC20.sol @@ -4,6 +4,12 @@ import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +/** +* @title UniversalERC20 library +* @author Aave inspired by @k06a (Anton Bukov) +* @note original version: https://github.com/CryptoManiacsZone/1inchProtocol/blob/master/contracts/UniversalERC20.sol +* @dev Provides unified interface for ERC20 and native ETH operations +**/ library UniversalERC20 { using SafeMath for uint256; using SafeERC20 for IERC20; @@ -11,17 +17,24 @@ library UniversalERC20 { IERC20 private constant ZERO_ADDRESS = IERC20( 0x0000000000000000000000000000000000000000 ); + // @notice mock address of ETH IERC20 private constant ETH_ADDRESS = IERC20( 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ); + /** + * @dev Moves amount of asset from caller to recipient + * @param token underlying asset address + * @param to asset recipient + * @param amount to move + **/ function universalTransfer( IERC20 token, address to, uint256 amount - ) internal returns (bool) { + ) internal { if (amount == 0) { - return true; + return; } if (isETH(token)) { @@ -30,9 +43,16 @@ library UniversalERC20 { } else { token.safeTransfer(to, amount); } - return true; } + /** + * @dev Moves amount of asset from sender to recipient + * @param token underlying asset address + * @param from asset sender + * @param to asset recipient + * @param amount to move + * @param returnExcess if true returns exceeded amount to sender + **/ function universalTransferFrom( IERC20 token, address from, @@ -67,6 +87,11 @@ library UniversalERC20 { } } + /** + * @dev Moves amount of asset from caller to this contract + * @param token underlying asset address + * @param amount to move + **/ function universalTransferFromSenderToThis(IERC20 token, uint256 amount) internal { @@ -88,6 +113,12 @@ library UniversalERC20 { } } + /** + * @dev Sets the allowance over the caller's tokens to recipient address. + * @param token underlying asset address + * @param to allowance recipient + * @param amount of the allowance + **/ function universalApprove( IERC20 token, address to, @@ -100,7 +131,12 @@ library UniversalERC20 { token.safeApprove(to, amount); } } - + /** + * @dev Returns the amount of underlying asset owned by address + * @param token underlying asset address + * @param who address to check + * @returns balance of the who address + **/ function universalBalanceOf(IERC20 token, address who) internal view @@ -113,6 +149,11 @@ library UniversalERC20 { } } + /** + * @dev Returns decimals of underlying asset + * @param token underlying asset address + * @returns decimals + **/ function universalDecimals(IERC20 token) internal view returns (uint256) { if (isETH(token)) { return 18; @@ -130,6 +171,11 @@ library UniversalERC20 { return (success && data.length > 0) ? abi.decode(data, (uint256)) : 18; } + /** + * @dev Checks is underlying asset ETH or not + * @param token underlying asset address + * @returns boolean + **/ function isETH(IERC20 token) internal pure returns (bool) { return (address(token) == address(ZERO_ADDRESS) || address(token) == address(ETH_ADDRESS)); diff --git a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol index 6b80ea1a..203439d2 100644 --- a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol +++ b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol @@ -36,7 +36,10 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase { //check the contract has the specified balance - require(_amount <= getBalanceInternal(address(this), _reserve), "Invalid balance for the contract"); + require( + _amount <= IERC20(_reserve).universalBalanceOf(address(this)), + "Invalid balance for the contract" + ); if(failExecution) { emit ExecutedWithFail(_reserve, _amount, _fee); From a41816b32c7624296c7532ec89f0c979a128a236 Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 3 Jun 2020 14:08:22 +0300 Subject: [PATCH 06/12] fix typos in docstrings --- contracts/libraries/UniversalERC20.sol | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/libraries/UniversalERC20.sol b/contracts/libraries/UniversalERC20.sol index 89283b9c..ab7cd579 100644 --- a/contracts/libraries/UniversalERC20.sol +++ b/contracts/libraries/UniversalERC20.sol @@ -7,7 +7,7 @@ import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; /** * @title UniversalERC20 library * @author Aave inspired by @k06a (Anton Bukov) -* @note original version: https://github.com/CryptoManiacsZone/1inchProtocol/blob/master/contracts/UniversalERC20.sol +* original version: https://github.com/CryptoManiacsZone/1inchProtocol/blob/master/contracts/UniversalERC20.sol * @dev Provides unified interface for ERC20 and native ETH operations **/ library UniversalERC20 { @@ -131,11 +131,12 @@ library UniversalERC20 { token.safeApprove(to, amount); } } + /** * @dev Returns the amount of underlying asset owned by address * @param token underlying asset address * @param who address to check - * @returns balance of the who address + * @return balance of the who address **/ function universalBalanceOf(IERC20 token, address who) internal @@ -152,7 +153,7 @@ library UniversalERC20 { /** * @dev Returns decimals of underlying asset * @param token underlying asset address - * @returns decimals + * @return decimals **/ function universalDecimals(IERC20 token) internal view returns (uint256) { if (isETH(token)) { @@ -174,7 +175,7 @@ library UniversalERC20 { /** * @dev Checks is underlying asset ETH or not * @param token underlying asset address - * @returns boolean + * @return boolean **/ function isETH(IERC20 token) internal pure returns (bool) { return (address(token) == address(ZERO_ADDRESS) || From 43ae2f36e729b177bfc2539fb81718605c5fafd0 Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 3 Jun 2020 15:48:59 +0300 Subject: [PATCH 07/12] update universalTransferFrom logic, and comments --- contracts/libraries/UniversalERC20.sol | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/contracts/libraries/UniversalERC20.sol b/contracts/libraries/UniversalERC20.sol index ab7cd579..429d7079 100644 --- a/contracts/libraries/UniversalERC20.sol +++ b/contracts/libraries/UniversalERC20.sol @@ -22,6 +22,8 @@ library UniversalERC20 { 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ); + uint256 private constant DEFAULT_TRANSFER_GAS = 50000; + /** * @dev Moves amount of asset from caller to recipient * @param token underlying asset address @@ -38,7 +40,10 @@ library UniversalERC20 { } if (isETH(token)) { - (bool result, ) = payable(to).call{value: amount, gas: 50000}(""); + (bool result, ) = payable(to).call{ + value: amount, + gas: DEFAULT_TRANSFER_GAS + }(""); require(result, "ETH_TRANSFER_FAILED"); } else { token.safeTransfer(to, amount); @@ -47,6 +52,7 @@ library UniversalERC20 { /** * @dev Moves amount of asset from sender to recipient + * in terms of ETH it redirects amount in transaction to recipient * @param token underlying asset address * @param from asset sender * @param to asset recipient @@ -70,15 +76,16 @@ library UniversalERC20 { "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}( - "" - ); + (bool result, ) = payable(to).call{ + value: amount, + gas: DEFAULT_TRANSFER_GAS + }(""); require(result, "ETH_TRANSFER_FAILED"); } if (returnExcess && msg.value > amount) { - (bool result, ) = msg.sender.call{ + (bool result, ) = payable(from).call{ value: msg.value.sub(amount), - gas: 50000 + gas: DEFAULT_TRANSFER_GAS }(""); require(result, "ETH_TRANSFER_FAILED"); } From 199d386b3e226e8dc5444457922b789264567ac0 Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 3 Jun 2020 15:51:17 +0300 Subject: [PATCH 08/12] removal of transferToReserve method --- contracts/lendingpool/LendingPool.sol | 25 +++++++++++------ contracts/lendingpool/LendingPoolCore.sol | 27 ------------------- .../LendingPoolLiquidationManager.sol | 9 +++++-- 3 files changed, 24 insertions(+), 37 deletions(-) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index e8d0b337..0a209f24 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -315,8 +315,13 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { //minting AToken to user 1:1 with the specific exchange rate aToken.mintOnDeposit(msg.sender, _amount); - //transfer to the core contract - core.transferToReserve{value: msg.value}(_reserve, msg.sender, _amount); + //transfer to the pool + if (!IERC20(_reserve).isETH()) { //TODO: review needed, most probably we can remove it + require( + msg.value == 0, + "User is sending ETH along with the ERC20 transfer." + ); + IERC20(_reserve).universalTransferFromSenderToThis(_amount); //solium-disable-next-line emit Deposit(_reserve, msg.sender, _amount, _referralCode, block.timestamp); @@ -623,13 +628,17 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { } //sending the total msg.value if the transfer is ETH. - //the transferToReserve() function will take care of sending the + //the universalTransferFromSenderToThis() function will take care of sending the //excess ETH back to the caller - core.transferToReserve{ value: vars.isETH ? msg.value.sub(vars.originationFee) : 0 }( - _reserve, - msg.sender, - vars.paybackAmountMinusFees - ); + if (!IERC20(_reserve).isETH()) { //TODO: review needed, most probably we can remove it + require( + msg.value == 0, + "User is sending ETH along with the ERC20 transfer." + ); + } + IERC20(_reserve).universalTransferFromSenderToThis{ + value: vars.isETH ? msg.value.sub(vars.originationFee) : 0 + }(vars.paybackAmountMinusFees); emit Repay( _reserve, diff --git a/contracts/lendingpool/LendingPoolCore.sol b/contracts/lendingpool/LendingPoolCore.sol index c99c9743..5273d028 100644 --- a/contracts/lendingpool/LendingPoolCore.sol +++ b/contracts/lendingpool/LendingPoolCore.sol @@ -472,33 +472,6 @@ contract LendingPoolCore is VersionedInitializable { IERC20(_token).universalTransfer(_feeAddress, _amount); } - /** - * @dev transfers an amount from a user to the destination reserve - * @param _reserve the address of the reserve where the amount is being transferred - * @param _user the address of the user from where the transfer is happening - * @param _amount the amount being transferred - **/ - function transferToReserve(address _reserve, address payable _user, uint256 _amount) - external - payable - onlyLendingPool - { - IERC20 reserve = IERC20(_reserve); - - if (!reserve.isETH()) { - require( - msg.value == 0, - "User is sending ETH along with the ERC20 transfer." - ); - } else { - require( - msg.value >= _amount, - "The amount and the value sent to deposit do not match" - ); - } - reserve.universalTransferFrom(_user, address(this), _amount, true); - } - /** * @notice data access functions **/ diff --git a/contracts/lendingpool/LendingPoolLiquidationManager.sol b/contracts/lendingpool/LendingPoolLiquidationManager.sol index a5f9bea7..fb28f4c0 100644 --- a/contracts/lendingpool/LendingPoolLiquidationManager.sol +++ b/contracts/lendingpool/LendingPoolLiquidationManager.sol @@ -246,11 +246,16 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl //otherwise receives the underlying asset //burn the equivalent amount of atoken collateralAtoken.burnOnLiquidation(_user, maxCollateralToLiquidate); - core.transferToUser(_collateral, msg.sender, maxCollateralToLiquidate); + core.transferToUser(_collateral, msg.sender, maxCollateralToLiquidate); //TODO: update to universal transfer } //transfers the principal currency to the pool - core.transferToReserve{value: msg.value}(_reserve, msg.sender, vars.actualAmountToLiquidate); + IERC20(_reserve).universalTransferFrom( + msg.sender, + addressesProvider.getLendingPool(), + vars.actualAmountToLiquidate, + true + ); if (vars.feeLiquidated > 0) { //if there is enough collateral to liquidate the fee, first transfer burn an equivalent amount of From 43b5c2c8326262f14d84a6de865325a875c263c8 Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 3 Jun 2020 17:57:41 +0300 Subject: [PATCH 09/12] compilation errors fixes, removed: getReserveAvailableLiquidity, transferToUser, transferToFeeCollectionAddress, transferFlashLoanProtocolFeeInternal --- contracts/fees/MockKyberProxy.sol | 2 +- contracts/fees/MockOneSplit.sol | 2 +- contracts/lendingpool/LendingPool.sol | 63 ++++++---- contracts/lendingpool/LendingPoolCore.sol | 109 +++--------------- .../lendingpool/LendingPoolDataProvider.sol | 5 +- .../LendingPoolLiquidationManager.sol | 23 ++-- 6 files changed, 73 insertions(+), 131 deletions(-) diff --git a/contracts/fees/MockKyberProxy.sol b/contracts/fees/MockKyberProxy.sol index 044c4b20..4dc31f3a 100644 --- a/contracts/fees/MockKyberProxy.sol +++ b/contracts/fees/MockKyberProxy.sol @@ -36,7 +36,7 @@ contract MockKyberProxy { ) external payable returns (uint256) { require(tokenToBurn.mint(1 ether), "TRADE_WITH_HINT. Reverted mint()"); if (!_fromToken.isETH()) { - _fromToken.universalTransferFrom(msg.sender, address(this), _amount, false); + _fromToken.universalTransferFromSenderToThis(_amount); } tokenToBurn.universalTransfer(msg.sender, 1 ether); return 1 ether; diff --git a/contracts/fees/MockOneSplit.sol b/contracts/fees/MockOneSplit.sol index 96d8fc74..d8791e21 100644 --- a/contracts/fees/MockOneSplit.sol +++ b/contracts/fees/MockOneSplit.sol @@ -55,7 +55,7 @@ contract MockOneSplit is IOneSplit { ) public override payable { require(tokenToBurn.mint(10000 ether), "TRADE_WITH_HINT. Reverted mint()"); if (!fromToken.isETH()) { - fromToken.universalTransferFrom(msg.sender, address(this), amount, false); + fromToken.universalTransferFromSenderToThis(amount); } tokenToBurn.universalTransfer(msg.sender, 10000 ether); } diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 0a209f24..fcf73c82 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -321,6 +321,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { msg.value == 0, "User is sending ETH along with the ERC20 transfer." ); + } IERC20(_reserve).universalTransferFromSenderToThis(_amount); //solium-disable-next-line @@ -347,7 +348,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { onlyActiveReserve(_reserve) onlyAmountGreaterThanZero(_amount) { - uint256 currentAvailableLiquidity = core.getReserveAvailableLiquidity(_reserve); + uint256 currentAvailableLiquidity = IERC20(_reserve).universalBalanceOf(address(this)); require( currentAvailableLiquidity >= _amount, "There is not enough liquidity available to redeem" @@ -355,7 +356,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { core.updateStateOnRedeem(_reserve, _user, _amount, _aTokenBalanceAfterRedeem == 0); - core.transferToUser(_reserve, _user, _amount); + IERC20(_reserve).universalTransfer(_user, _amount); //solium-disable-next-line emit RedeemUnderlying(_reserve, _user, _amount, block.timestamp); @@ -420,7 +421,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { vars.rateMode = CoreLibrary.InterestRateMode(_interestRateMode); //check that the amount is available in the reserve - vars.availableLiquidity = core.getReserveAvailableLiquidity(_reserve); + vars.availableLiquidity = IERC20(_reserve).universalBalanceOf(address(this)); // TODO: review needed, most probably useless require( vars.availableLiquidity >= _amount, @@ -501,7 +502,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { ); //if we reached this point, we can transfer - core.transferToUser(_reserve, msg.sender, _amount); + IERC20(_reserve).universalTransfer(msg.sender, _amount); emit Borrow( _reserve, @@ -586,11 +587,17 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { false ); - core.transferToFeeCollectionAddress{ value: vars.isETH ? vars.paybackAmount : 0 }( - _reserve, + if (!vars.isETH) { // TODO: review needed, maybe we should not care + require( + msg.value == 0, + "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" + ); + } + IERC20(_reserve).universalTransferFrom( _onBehalfOf, + addressesProvider.getTokenDistributor(), vars.paybackAmount, - addressesProvider.getTokenDistributor() + false ); emit Repay( @@ -618,27 +625,38 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { ); //if the user didn't repay the origination fee, transfer the fee to the fee collection address - if(vars.originationFee > 0) { - core.transferToFeeCollectionAddress{ value: vars.isETH ? vars.originationFee : 0 }( - _reserve, + if (vars.originationFee > 0) { + if (!vars.isETH) { // TODO: review needed, maybe we should not care + require( + msg.value == 0, + "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" + ); + } + IERC20(_reserve).universalTransferFrom( _onBehalfOf, + addressesProvider.getTokenDistributor(), vars.originationFee, - addressesProvider.getTokenDistributor() + false ); } - //sending the total msg.value if the transfer is ETH. - //the universalTransferFromSenderToThis() function will take care of sending the - //excess ETH back to the caller - if (!IERC20(_reserve).isETH()) { //TODO: review needed, most probably we can remove it + if (vars.isETH) { //TODO: review needed, most probably we can remove it require( msg.value == 0, "User is sending ETH along with the ERC20 transfer." ); } - IERC20(_reserve).universalTransferFromSenderToThis{ - value: vars.isETH ? msg.value.sub(vars.originationFee) : 0 - }(vars.paybackAmountMinusFees); + IERC20(_reserve).universalTransferFromSenderToThis(vars.paybackAmountMinusFees); + + if (vars.isETH) { + uint256 exceedAmount = msg.value + .sub(vars.originationFee) + .sub(vars.paybackAmountMinusFees); + //send excess ETH back to the caller in needed + if (exceedAmount > 0) { + IERC20(_reserve).universalTransfer(msg.sender, exceedAmount); + } + } emit Repay( _reserve, @@ -881,10 +899,8 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { //get the FlashLoanReceiver instance IFlashLoanReceiver receiver = IFlashLoanReceiver(_receiver); - address payable userPayable = payable(_receiver); - //transfer funds to the receiver - core.transferToUser(_reserve, userPayable, _amount); + IERC20(_reserve).universalTransfer(_receiver, _amount); //execute action of the receiver receiver.executeOperation(_reserve, _amount, amountFee, _params); @@ -897,6 +913,11 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { "The actual balance of the protocol is inconsistent" ); + // transfer protocol fee to the Distributor contract + IERC20(_reserve).universalTransfer( + addressesProvider.getTokenDistributor(), + protocolFee + ); core.updateStateOnFlashLoan( _reserve, availableLiquidityBefore, diff --git a/contracts/lendingpool/LendingPoolCore.sol b/contracts/lendingpool/LendingPoolCore.sol index 5273d028..b2ad2f65 100644 --- a/contracts/lendingpool/LendingPoolCore.sol +++ b/contracts/lendingpool/LendingPoolCore.sol @@ -174,8 +174,6 @@ contract LendingPoolCore is VersionedInitializable { uint256 _income, uint256 _protocolFee ) external onlyLendingPool { - transferFlashLoanProtocolFeeInternal(_reserve, _protocolFee); - //compounding the cumulated interest reserves[_reserve].updateCumulativeIndexes(); @@ -408,70 +406,6 @@ contract LendingPoolCore is VersionedInitializable { } - /** - * @dev transfers to the user a specific amount from the reserve. - * @param _reserve the address of the reserve where the transfer is happening - * @param _user the address of the user receiving the transfer - * @param _amount the amount being transferred - **/ - function transferToUser(address _reserve, address payable _user, uint256 _amount) - external - onlyLendingPool - { - IERC20(_reserve).universalTransfer(_user, _amount); - } - - /** - * @dev transfers the protocol fees to the fees collection address - * @param _token the address of the token being transferred - * @param _user the address of the user from where the transfer is happening - * @param _amount the amount being transferred - * @param _feeAddress the fee receiver address - **/ - - function transferToFeeCollectionAddress( - address _token, - address _user, - uint256 _amount, - address _feeAddress - ) external payable onlyLendingPool { - IERC20 token = IERC20(_token); - - if (!token.isETH()) { - require( - msg.value == 0, - "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" - ); - } else { - require(msg.value >= _amount, "The amount and the value sent to deposit do not match"); - } - IERC20(_token).universalTransferFrom( - _user, - _feeAddress, - _amount, - false - ); - } - - /** - * @dev transfers the fees to the fees collection address in the case of liquidation - * @param _token the address of the token being transferred - * @param _amount the amount being transferred - * @param _feeAddress the fee receiver address - **/ - function liquidateFee( - address _token, - uint256 _amount, - address _feeAddress - ) external payable onlyLendingPool { - require( - msg.value == 0, - "Fee liquidation does not require any transfer of value" - ); - - IERC20(_token).universalTransfer(_feeAddress, _amount); - } - /** * @notice data access functions **/ @@ -567,15 +501,6 @@ contract LendingPoolCore is VersionedInitializable { return reserve.aTokenAddress; } - /** - * @dev gets the available liquidity in the reserve. The available liquidity is the balance of the core contract - * @param _reserve the reserve address - * @return the available liquidity - **/ - function getReserveAvailableLiquidity(address _reserve) public view returns (uint256) { - return IERC20(_reserve).universalBalanceOf(address(this)); - } - /** * @dev gets the total liquidity in the reserve. The total liquidity is the balance of the core contract + total borrows * @param _reserve the reserve address @@ -583,7 +508,10 @@ contract LendingPoolCore is VersionedInitializable { **/ function getReserveTotalLiquidity(address _reserve) public view returns (uint256) { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; - return getReserveAvailableLiquidity(_reserve).add(reserve.getTotalBorrows()); + + return IERC20(_reserve) + .universalBalanceOf(addressesProvider.getLendingPool()) + .add(reserve.getTotalBorrows()); } /** @@ -850,8 +778,9 @@ contract LendingPoolCore is VersionedInitializable { return 0; } - uint256 availableLiquidity = getReserveAvailableLiquidity(_reserve); - + uint256 availableLiquidity = IERC20(_reserve).universalBalanceOf( + addressesProvider.getLendingPool() + ); return totalBorrows.rayDiv(availableLiquidity.add(totalBorrows)); } @@ -1680,14 +1609,15 @@ contract LendingPoolCore is VersionedInitializable { CoreLibrary.ReserveData storage reserve = reserves[_reserve]; uint256 currentAvgStableRate = reserve.currentAverageStableBorrowRate; + uint256 avialableLiquidity = IERC20(_reserve).universalBalanceOf(addressesProvider.getLendingPool()) + .add(_liquidityAdded) + .sub(_liquidityTaken); (uint256 newLiquidityRate, uint256 newStableRate, uint256 newVariableRate) = IReserveInterestRateStrategy( - reserve - .interestRateStrategyAddress - ) - .calculateInterestRates( + reserve.interestRateStrategyAddress + ).calculateInterestRates( _reserve, - getReserveAvailableLiquidity(_reserve).add(_liquidityAdded).sub(_liquidityTaken), + avialableLiquidity, reserve.totalBorrowsStable, reserve.totalBorrowsVariable, currentAvgStableRate @@ -1711,19 +1641,6 @@ contract LendingPoolCore is VersionedInitializable { ); } - /** - * @dev transfers to the protocol fees of a flashloan to the fees collection address - * @param _token the address of the token being transferred - * @param _amount the amount being transferred - **/ - - function transferFlashLoanProtocolFeeInternal(address _token, uint256 _amount) internal { - IERC20(_token).universalTransfer( - addressesProvider.getTokenDistributor(), - _amount - ); - } - /** * @dev updates the internal configuration of the core **/ diff --git a/contracts/lendingpool/LendingPoolDataProvider.sol b/contracts/lendingpool/LendingPoolDataProvider.sol index d1c7318b..4cef745d 100644 --- a/contracts/lendingpool/LendingPoolDataProvider.sol +++ b/contracts/lendingpool/LendingPoolDataProvider.sol @@ -2,6 +2,7 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol"; import "../libraries/CoreLibrary.sol"; @@ -10,6 +11,7 @@ import "../libraries/WadRayMath.sol"; import "../interfaces/IPriceOracleGetter.sol"; import "../interfaces/IFeeProvider.sol"; import "../tokenization/AToken.sol"; +import "../libraries/UniversalERC20.sol"; import "./LendingPoolCore.sol"; @@ -22,6 +24,7 @@ import "./LendingPoolCore.sol"; contract LendingPoolDataProvider is VersionedInitializable { using SafeMath for uint256; using WadRayMath for uint256; + using UniversalERC20 for IERC20; LendingPoolCore public core; LendingPoolAddressesProvider public addressesProvider; @@ -395,7 +398,7 @@ contract LendingPoolDataProvider is VersionedInitializable { ) { totalLiquidity = core.getReserveTotalLiquidity(_reserve); - availableLiquidity = core.getReserveAvailableLiquidity(_reserve); + availableLiquidity = IERC20(_reserve).universalBalanceOf(addressesProvider.getLendingPool()); totalBorrowsStable = core.getReserveTotalBorrowsStable(_reserve); totalBorrowsVariable = core.getReserveTotalBorrowsVariable(_reserve); liquidityRate = core.getReserveCurrentLiquidityRate(_reserve); diff --git a/contracts/lendingpool/LendingPoolLiquidationManager.sol b/contracts/lendingpool/LendingPoolLiquidationManager.sol index fb28f4c0..9543a7fc 100644 --- a/contracts/lendingpool/LendingPoolLiquidationManager.sol +++ b/contracts/lendingpool/LendingPoolLiquidationManager.sol @@ -2,6 +2,7 @@ pragma solidity ^0.6.8; import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; @@ -15,6 +16,7 @@ import "../libraries/WadRayMath.sol"; import "./LendingPoolCore.sol"; import "./LendingPoolDataProvider.sol"; import "../interfaces/IPriceOracleGetter.sol"; +import "../libraries/UniversalERC20.sol"; /** * @title LendingPoolLiquidationManager contract @@ -25,6 +27,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl using SafeMath for uint256; using WadRayMath for uint256; using Address for address; + using UniversalERC20 for IERC20; LendingPoolAddressesProvider public addressesProvider; LendingPoolCore core; @@ -216,7 +219,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl //if liquidator reclaims the underlying asset, we make sure there is enough available collateral in the reserve if (!_receiveAToken) { - uint256 currentAvailableCollateral = core.getReserveAvailableLiquidity(_collateral); + uint256 currentAvailableCollateral = IERC20(_collateral).universalBalanceOf(address(this)); if (currentAvailableCollateral < maxCollateralToLiquidate) { return ( uint256(LiquidationErrors.NOT_ENOUGH_LIQUIDITY), @@ -246,15 +249,14 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl //otherwise receives the underlying asset //burn the equivalent amount of atoken collateralAtoken.burnOnLiquidation(_user, maxCollateralToLiquidate); - core.transferToUser(_collateral, msg.sender, maxCollateralToLiquidate); //TODO: update to universal transfer + // because liquidate function executed as delegated call this will be LendingPool contract address + // and funds will be transferred from there + IERC20(_collateral).universalTransfer(msg.sender, maxCollateralToLiquidate); } //transfers the principal currency to the pool - IERC20(_reserve).universalTransferFrom( - msg.sender, - addressesProvider.getLendingPool(), - vars.actualAmountToLiquidate, - true + IERC20(_reserve).universalTransferFromSenderToThis( + vars.actualAmountToLiquidate ); if (vars.feeLiquidated > 0) { @@ -263,10 +265,9 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl collateralAtoken.burnOnLiquidation(_user, vars.liquidatedCollateralForFee); //then liquidate the fee by transferring it to the fee collection address - core.liquidateFee( - _collateral, - vars.liquidatedCollateralForFee, - addressesProvider.getTokenDistributor() + IERC20(_collateral).universalTransfer( + addressesProvider.getTokenDistributor(), + vars.liquidatedCollateralForFee ); emit OriginationFeeLiquidated( From a281bdba97df2f3c1f0240ec63aeee272b1d8db7 Mon Sep 17 00:00:00 2001 From: andyk Date: Thu, 4 Jun 2020 10:57:08 +0300 Subject: [PATCH 10/12] update universalTransferFromSenderToThis with returnExcess --- contracts/fees/MockKyberProxy.sol | 2 +- contracts/fees/MockOneSplit.sol | 2 +- contracts/lendingpool/LendingPool.sol | 6 +++--- contracts/lendingpool/LendingPoolLiquidationManager.sol | 2 +- contracts/libraries/UniversalERC20.sol | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/fees/MockKyberProxy.sol b/contracts/fees/MockKyberProxy.sol index 4dc31f3a..c630c890 100644 --- a/contracts/fees/MockKyberProxy.sol +++ b/contracts/fees/MockKyberProxy.sol @@ -36,7 +36,7 @@ contract MockKyberProxy { ) external payable returns (uint256) { require(tokenToBurn.mint(1 ether), "TRADE_WITH_HINT. Reverted mint()"); if (!_fromToken.isETH()) { - _fromToken.universalTransferFromSenderToThis(_amount); + _fromToken.universalTransferFromSenderToThis(_amount, true); } tokenToBurn.universalTransfer(msg.sender, 1 ether); return 1 ether; diff --git a/contracts/fees/MockOneSplit.sol b/contracts/fees/MockOneSplit.sol index d8791e21..eecc89e7 100644 --- a/contracts/fees/MockOneSplit.sol +++ b/contracts/fees/MockOneSplit.sol @@ -55,7 +55,7 @@ contract MockOneSplit is IOneSplit { ) public override payable { require(tokenToBurn.mint(10000 ether), "TRADE_WITH_HINT. Reverted mint()"); if (!fromToken.isETH()) { - fromToken.universalTransferFromSenderToThis(amount); + fromToken.universalTransferFromSenderToThis(amount, true); } tokenToBurn.universalTransfer(msg.sender, 10000 ether); } diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index fcf73c82..775c1bb7 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -322,7 +322,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { "User is sending ETH along with the ERC20 transfer." ); } - IERC20(_reserve).universalTransferFromSenderToThis(_amount); + IERC20(_reserve).universalTransferFromSenderToThis(_amount, true); //solium-disable-next-line emit Deposit(_reserve, msg.sender, _amount, _referralCode, block.timestamp); @@ -646,13 +646,13 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { "User is sending ETH along with the ERC20 transfer." ); } - IERC20(_reserve).universalTransferFromSenderToThis(vars.paybackAmountMinusFees); + IERC20(_reserve).universalTransferFromSenderToThis(vars.paybackAmountMinusFees, false); if (vars.isETH) { uint256 exceedAmount = msg.value .sub(vars.originationFee) .sub(vars.paybackAmountMinusFees); - //send excess ETH back to the caller in needed + //send excess ETH back to the caller if needed if (exceedAmount > 0) { IERC20(_reserve).universalTransfer(msg.sender, exceedAmount); } diff --git a/contracts/lendingpool/LendingPoolLiquidationManager.sol b/contracts/lendingpool/LendingPoolLiquidationManager.sol index 9543a7fc..6876ddeb 100644 --- a/contracts/lendingpool/LendingPoolLiquidationManager.sol +++ b/contracts/lendingpool/LendingPoolLiquidationManager.sol @@ -256,7 +256,7 @@ contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializabl //transfers the principal currency to the pool IERC20(_reserve).universalTransferFromSenderToThis( - vars.actualAmountToLiquidate + vars.actualAmountToLiquidate, true ); if (vars.feeLiquidated > 0) { diff --git a/contracts/libraries/UniversalERC20.sol b/contracts/libraries/UniversalERC20.sol index 429d7079..03236026 100644 --- a/contracts/libraries/UniversalERC20.sol +++ b/contracts/libraries/UniversalERC20.sol @@ -99,7 +99,7 @@ library UniversalERC20 { * @param token underlying asset address * @param amount to move **/ - function universalTransferFromSenderToThis(IERC20 token, uint256 amount) + function universalTransferFromSenderToThis(IERC20 token, uint256 amount, bool returnExcess) internal { if (amount == 0) { @@ -107,7 +107,7 @@ library UniversalERC20 { } if (isETH(token)) { - if (msg.value > amount) { + if (msg.value > amount && returnExcess) { // Return remainder if exist (bool result, ) = msg.sender.call{ value: msg.value.sub(amount), From d099d3c20e4b70ea254c934c4434f0e6d205f989 Mon Sep 17 00:00:00 2001 From: andyk Date: Fri, 5 Jun 2020 10:35:29 +0300 Subject: [PATCH 11/12] fix isETH condition in repay --- contracts/lendingpool/LendingPool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 775c1bb7..4c2616e4 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -640,7 +640,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { ); } - if (vars.isETH) { //TODO: review needed, most probably we can remove it + if (!vars.isETH) { //TODO: review needed, most probably we can remove it require( msg.value == 0, "User is sending ETH along with the ERC20 transfer." From 951bc6ebf60325c45744599aaba478d890a7e276 Mon Sep 17 00:00:00 2001 From: andyk Date: Fri, 5 Jun 2020 11:23:48 +0300 Subject: [PATCH 12/12] optimize 0 ETH --- contracts/lendingpool/LendingPool.sol | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 4c2616e4..6945d2b0 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -572,7 +572,7 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { } require( - !vars.isETH || msg.value >= vars.paybackAmount, + (!vars.isETH && msg.value == 0) || msg.value >= vars.paybackAmount, "Invalid msg.value sent for the repayment" ); @@ -587,12 +587,6 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { false ); - if (!vars.isETH) { // TODO: review needed, maybe we should not care - require( - msg.value == 0, - "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" - ); - } IERC20(_reserve).universalTransferFrom( _onBehalfOf, addressesProvider.getTokenDistributor(), @@ -626,12 +620,6 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { //if the user didn't repay the origination fee, transfer the fee to the fee collection address if (vars.originationFee > 0) { - if (!vars.isETH) { // TODO: review needed, maybe we should not care - require( - msg.value == 0, - "User is sending ETH along with the ERC20 transfer. Check the value attribute of the transaction" - ); - } IERC20(_reserve).universalTransferFrom( _onBehalfOf, addressesProvider.getTokenDistributor(), @@ -640,12 +628,6 @@ contract LendingPool is ReentrancyGuard, VersionedInitializable { ); } - if (!vars.isETH) { //TODO: review needed, most probably we can remove it - require( - msg.value == 0, - "User is sending ETH along with the ERC20 transfer." - ); - } IERC20(_reserve).universalTransferFromSenderToThis(vars.paybackAmountMinusFees, false); if (vars.isETH) {