// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "@openzeppelin/contracts/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";

import "./interfaces/IWeth.sol";
import "./utils/LibConstant.sol";
import "./utils/Ownable.sol";

contract MarketMakerProxy is Ownable {
    using SafeERC20 for IERC20;
    using Address for address payable;

    event ChangeSigner(address);
    event UpdateWhitelist(address, bool);
    event WrapETH(uint256);
    event WithdrawETH(uint256);

    // bytes4(keccak256("isValidSignature(bytes32,bytes)")
    bytes4 public constant EIP1271_MAGICVALUE = 0x1626ba7e;
    IWETH public immutable WETH;

    address public signer;
    mapping(address => bool) public isWithdrawWhitelist;

    constructor(
        address _owner,
        address _signer,
        IWETH _weth
    ) Ownable(_owner) {
        require(_signer != address(0), "MarketMakerProxy: zero address");
        signer = _signer;
        WETH = _weth;
    }

    receive() external payable {}

    function setSigner(address _signer) external onlyOwner {
        require(_signer != address(0), "MarketMakerProxy: zero address");
        emit ChangeSigner(_signer);
        signer = _signer;
    }

    function setAllowance(address[] calldata tokenAddrs, address spender) external onlyOwner {
        for (uint256 i = 0; i < tokenAddrs.length; i++) {
            IERC20(tokenAddrs[i]).safeApprove(spender, LibConstant.MAX_UINT);
        }
    }

    function closeAllowance(address[] calldata tokenAddrs, address spender) external onlyOwner {
        for (uint256 i = 0; i < tokenAddrs.length; i++) {
            IERC20(tokenAddrs[i]).safeApprove(spender, 0);
        }
    }

    function updateWithdrawWhitelist(address _addr, bool _enabled) external onlyOwner {
        require(_addr != address(0), "MarketMakerProxy: zero address");
        isWithdrawWhitelist[_addr] = _enabled;
        emit UpdateWhitelist(_addr, _enabled);
    }

    function wrapETH() external onlyOwner {
        uint256 balance = address(this).balance;
        if (balance > 0) {
            WETH.deposit{ value: balance }();
            emit WrapETH(balance);
        }
    }

    function withdrawToken(
        address token,
        address to,
        uint256 amount
    ) external onlyOwner {
        require(isWithdrawWhitelist[to], "MarketMakerProxy: not in withdraw whitelist");
        IERC20(token).safeTransfer(to, amount);
    }

    function withdrawETH(address payable to, uint256 amount) external onlyOwner {
        require(isWithdrawWhitelist[to], "MarketMakerProxy: not in withdraw whitelist");
        to.sendValue(amount);
        emit WithdrawETH(amount);
    }

    function isValidSignature(bytes32 dataHash, bytes calldata signature) external view returns (bytes4) {
        require(signer == ECDSA.recover(dataHash, signature), "MarketMakerProxy: invalid signature");
        return EIP1271_MAGICVALUE;
    }
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_signer","type":"address"},{"internalType":"contract IWETH","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"}],"name":"ChangeSigner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"bool","name":"","type":"bool"}],"name":"UpdateWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"WithdrawETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"WrapETH","type":"event"},{"inputs":[],"name":"EIP1271_MAGICVALUE","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokenAddrs","type":"address[]"},{"internalType":"address","name":"spender","type":"address"}],"name":"closeAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataHash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWithdrawWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokenAddrs","type":"address[]"},{"internalType":"address","name":"spender","type":"address"}],"name":"setAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"},{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"updateWithdrawWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wrapETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]