mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
migration to 1inch UniversalERC20
This commit is contained in:
parent
a683c24dec
commit
fea677a607
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
137
contracts/libraries/UniversalERC20.sol
Normal file
137
contracts/libraries/UniversalERC20.sol
Normal 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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user