// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol'; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IWETH} from './interfaces/IWETH.sol'; import {IWETHGateway} from './interfaces/IWETHGateway.sol'; import {ILendingPool} from '../interfaces/ILendingPool.sol'; import {IAToken} from '../interfaces/IAToken.sol'; import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol'; import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol'; import {Helpers} from '../protocol/libraries/helpers/Helpers.sol'; import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; contract WETHGateway is IWETHGateway, Ownable { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using UserConfiguration for DataTypes.UserConfigurationMap; IWETH internal immutable WETH; /** * @dev Sets the WETH address and the LendingPoolAddressesProvider address. Infinite approves lending pool. * @param weth Address of the Wrapped Ether contract **/ constructor(address weth) public { WETH = IWETH(weth); } function authorizeLendingPool(address lendingPool) external onlyOwner { WETH.approve(lendingPool, uint256(-1)); } /** * @dev deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying asset (aTokens) * is minted. * @param lendingPool address of the targeted underlying lending pool * @param onBehalfOf address of the user who will receive the aTokens representing the deposit * @param referralCode integrators are assigned a referral code and can potentially receive rewards. **/ function depositETH( address lendingPool, address onBehalfOf, uint16 referralCode ) external payable override { WETH.deposit{value: msg.value}(); ILendingPool(lendingPool).deposit(address(WETH), msg.value, onBehalfOf, referralCode); } /** * @dev withdraws the WETH _reserves of msg.sender. * @param lendingPool address of the targeted underlying lending pool * @param amount amount of aWETH to withdraw and receive native ETH * @param to address of the user who will receive native ETH */ function withdrawETH( address lendingPool, uint256 amount, address to ) external override { IAToken aWETH = IAToken(ILendingPool(lendingPool).getReserveData(address(WETH)).aTokenAddress); uint256 userBalance = aWETH.balanceOf(msg.sender); uint256 amountToWithdraw = amount; // if amount is equal to uint(-1), the user wants to redeem everything if (amount == type(uint256).max) { amountToWithdraw = userBalance; } aWETH.transferFrom(msg.sender, address(this), amountToWithdraw); ILendingPool(lendingPool).withdraw(address(WETH), amountToWithdraw, address(this)); WETH.withdraw(amountToWithdraw); _safeTransferETH(to, amountToWithdraw); } /** * @dev repays a borrow on the WETH reserve, for the specified amount (or for the whole amount, if uint256(-1) is specified). * @param lendingPool address of the targeted underlying lending pool * @param amount the amount to repay, or uint256(-1) if the user wants to repay everything * @param rateMode the rate mode to repay * @param onBehalfOf the address for which msg.sender is repaying */ function repayETH( address lendingPool, uint256 amount, uint256 rateMode, address onBehalfOf ) external payable override { (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebtMemory( onBehalfOf, ILendingPool(lendingPool).getReserveData(address(WETH)) ); uint256 paybackAmount = DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt; if (amount < paybackAmount) { paybackAmount = amount; } require(msg.value >= paybackAmount, 'msg.value is less than repayment amount'); WETH.deposit{value: paybackAmount}(); ILendingPool(lendingPool).repay(address(WETH), msg.value, rateMode, onBehalfOf); // refund remaining dust eth if (msg.value > paybackAmount) _safeTransferETH(msg.sender, msg.value - paybackAmount); } /** * @dev borrow WETH, unwraps to ETH and send both the ETH and DebtTokens to msg.sender, via `approveDelegation` and onBehalf argument in `LendingPool.borrow`. * @param lendingPool address of the targeted underlying lending pool * @param amount the amount of ETH to borrow * @param interesRateMode the interest rate mode * @param referralCode integrators are assigned a referral code and can potentially receive rewards */ function borrowETH( address lendingPool, uint256 amount, uint256 interesRateMode, uint16 referralCode ) external override { ILendingPool(lendingPool).borrow( address(WETH), amount, interesRateMode, referralCode, msg.sender ); WETH.withdraw(amount); _safeTransferETH(msg.sender, amount); } /** * @dev withdraws the WETH _reserves of msg.sender. * @param lendingPool address of the targeted underlying lending pool * @param amount amount of aWETH to withdraw and receive native ETH * @param to address of the user who will receive native ETH * @param deadline validity deadline of permit and so depositWithPermit signature * @param permitV V parameter of ERC712 permit sig * @param permitR R parameter of ERC712 permit sig * @param permitS S parameter of ERC712 permit sig */ function withdrawETHWithPermit( address lendingPool, uint256 amount, address to, uint256 deadline, uint8 permitV, bytes32 permitR, bytes32 permitS ) external override { IAToken aWETH = IAToken(ILendingPool(lendingPool).getReserveData(address(WETH)).aTokenAddress); uint256 userBalance = aWETH.balanceOf(msg.sender); uint256 amountToWithdraw = amount; // if amount is equal to uint(-1), the user wants to redeem everything if (amount == type(uint256).max) { amountToWithdraw = userBalance; } // chosing to permit `amount`and not `amountToWithdraw`, easier for frontends, intregrators. aWETH.permit(msg.sender, address(this), amount, deadline, permitV, permitR, permitS); aWETH.transferFrom(msg.sender, address(this), amountToWithdraw); ILendingPool(lendingPool).withdraw(address(WETH), amountToWithdraw, address(this)); WETH.withdraw(amountToWithdraw); _safeTransferETH(to, amountToWithdraw); } /** * @dev transfer ETH to an address, revert if it fails. * @param to recipient of the transfer * @param value the amount to send */ function _safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, 'ETH_TRANSFER_FAILED'); } /** * @dev transfer ERC20 from the utility contract, for ERC20 recovery in case of stuck tokens due * direct transfers to the contract address. * @param token token to transfer * @param to recipient of the transfer * @param amount amount to send */ function emergencyTokenTransfer( address token, address to, uint256 amount ) external onlyOwner { IERC20(token).transfer(to, amount); } /** * @dev transfer native Ether from the utility contract, for native Ether recovery in case of stuck Ether * due selfdestructs or transfer ether to pre-computated contract address before deployment. * @param to recipient of the transfer * @param amount amount to send */ function emergencyEtherTransfer(address to, uint256 amount) external onlyOwner { _safeTransferETH(to, amount); } /** * @dev Get WETH address used by WETHGateway */ function getWETHAddress() external view returns (address) { return address(WETH); } /** * @dev Only WETH contract is allowed to transfer ETH here. Prevent other addresses to send Ether to this contract. */ receive() external payable { require(msg.sender == address(WETH), 'Receive not allowed'); } /** * @dev Revert fallback calls */ fallback() external payable { revert('Fallback not allowed'); } }