// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.5; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '../interfaces/IPeripheryPayments.sol'; import '../interfaces/external/IWNativeToken.sol'; import '../libraries/TransferHelper.sol'; import './PeripheryImmutableState.sol'; /// @dev Credit to Uniswap Labs under GPL-2.0-or-later license: /// https://github.com/Uniswap/v3-periphery abstract contract PeripheryPayments is IPeripheryPayments, PeripheryImmutableState { receive() external payable { require(msg.sender == WNativeToken, 'Not WNativeToken'); } function _balanceOfToken(address token) private view returns (uint256) { return (IERC20(token).balanceOf(address(this))); } /// @inheritdoc IPeripheryPayments function unwrapWNativeToken(uint256 amountMinimum, address recipient) external payable override { uint256 balanceWNativeToken = _balanceOfToken(WNativeToken); require(balanceWNativeToken >= amountMinimum, 'Insufficient WNativeToken'); if (balanceWNativeToken > 0) { IWNativeToken(WNativeToken).withdraw(balanceWNativeToken); TransferHelper.safeTransferNative(recipient, balanceWNativeToken); } } /// @inheritdoc IPeripheryPayments function sweepToken(address token, uint256 amountMinimum, address recipient) external payable override { uint256 balanceToken = _balanceOfToken(token); require(balanceToken >= amountMinimum, 'Insufficient token'); if (balanceToken > 0) { TransferHelper.safeTransfer(token, recipient, balanceToken); } } /// @inheritdoc IPeripheryPayments function refundNativeToken() external payable override { if (address(this).balance > 0) TransferHelper.safeTransferNative(msg.sender, address(this).balance); } /// @param token The token to pay /// @param payer The entity that must pay /// @param recipient The entity that will receive payment /// @param value The amount to pay function pay(address token, address payer, address recipient, uint256 value) internal { if (token == WNativeToken && address(this).balance >= value) { // pay with WNativeToken // "address(this).balance >= value" may unexpectedly become false (including due to frontrun) // so this function should be accompanied by a `refundNativeToken` in multicall to avoid potential loss of tokens IWNativeToken(WNativeToken).deposit{value: value}(); // wrap only what is needed to pay IWNativeToken(WNativeToken).transfer(recipient, value); } else if (payer == address(this)) { // pay with tokens already in the contract (for the exact input multihop case) TransferHelper.safeTransfer(token, recipient, value); } else { // pull payment TransferHelper.safeTransferFrom(token, payer, recipient, value); } } }