aave-protocol-v2/contracts/libraries/UniversalERC20.sol
2020-06-02 18:16:27 +03:00

138 lines
3.8 KiB
Solidity

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, "ETH_TRANSFER_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, "ETH_TRANSFER_FAILED");
}
if (returnExcess && msg.value > amount) {
(bool result, ) = msg.sender.call{
value: msg.value.sub(amount),
gas: 50000
}("");
require(result, "ETH_TRANSFER_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, "ETH_TRANSFER_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));
}
}