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 "../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;
}
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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),

View File

@ -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);
}
/**

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));
}
}