contract BaseModule is ReentrancyGuard, Module
{
    event Activated   (address indexed wallet);
    event Deactivated (address indexed wallet);

    modifier onlyFromWallet(address wallet) virtual
    {
        require(msg.sender == wallet, "NOT_FROM_WALLET");
        _;
    }

    modifier onlyFromMetaTx() virtual {
        require(msg.sender == address(this), "NOT_FROM_META_TX");
        _;
    }

    modifier onlyFromWalletOwner(address wallet) virtual {
        require(msg.sender == Wallet(wallet).owner(), "NOT_FROM_WALLET_OWNER");
        _;
    }

    modifier onlyFromMetaTxOrWalletOwner(address wallet) virtual {
        require(
            msg.sender == address(this) || msg.sender == Wallet(wallet).owner(),
            "NOT_FROM_METATX_OR_WALLET_OWNER");
        _;
    }

    modifier onlyFromMetaTxOrOwner(address owner) virtual {
        require(
            msg.sender == address(this) || msg.sender == owner,
            "NOT_FROM_METATX_OR_OWNER");
        _;
    }

    modifier onlyWalletOwner(address wallet, address addr) virtual {
        require(Wallet(wallet).owner() == addr, "NOT_WALLET_OWNER");
        _;
    }

    modifier notWalletOwner(address wallet, address addr) virtual {
        require(Wallet(wallet).owner() != addr, "IS_WALLET_OWNER");
        _;
    }

    function addModule(
        address wallet,
        address module
        )
        external
        nonReentrant
        onlyFromMetaTxOrWalletOwner(wallet)
    {
        Wallet(wallet).addModule(module);
    }

    /// @dev This method will cause an re-entry to the same module contract.
    function activate()
        external
        override
        virtual
    {
        address wallet = msg.sender;
        bindMethods(wallet);
        emit Activated(wallet);
    }

    /// @dev This method will cause an re-entry to the same module contract.
    function deactivate()
        external
        override
        virtual
    {
        address wallet = msg.sender;
        unbindMethods(wallet);
        emit Deactivated(wallet);
    }

    ///.@dev Gets the list of methods for binding to wallets.
    ///      Sub-contracts should override this method to provide methods for
    ///      wallet binding.
    /// @return methods A list of method selectors for binding to the wallet
    ///         when this module is activated for the wallet.
    function bindableMethods()
        public
        pure
        virtual
        returns (bytes4[] memory methods)
    {
    }

    // ===== internal & private methods =====

    /// @dev Binds all methods to the given wallet.
    function bindMethods(address wallet)
        internal
    {
        Wallet w = Wallet(wallet);
        bytes4[] memory methods = bindableMethods();
        for (uint i = 0; i < methods.length; i++) {
            w.bindMethod(methods[i], address(this));
        }
    }

    /// @dev Unbinds all methods from the given wallet.
    function unbindMethods(address wallet)
        internal
    {
        Wallet w = Wallet(wallet);
        bytes4[] memory methods = bindableMethods();
        for (uint i = 0; i < methods.length; i++) {
            w.bindMethod(methods[i], address(0));
        }
    }

    function transactCall(
        address wallet,
        address to,
        uint    value,
        bytes   memory data
        )
        internal
        returns (bytes memory)
    {
        return Wallet(wallet).transact(uint8(1), to, value, data);
    }

    // Special case for transactCall to support transfers on "bad" ERC20 tokens
    function transactTokenTransfer(
        address wallet,
        address token,
        address to,
        uint    amount
        )
        internal
        returns (bool success)
    {
        bytes memory txData = abi.encodeWithSelector(
            ERC20(0).transfer.selector,
            to,
            amount
        );
        bytes memory returnData = transactCall(wallet, token, 0, txData);
        // `transactCall` will revert if the call was unsuccessful.
        // The only extra check we have to do is verify if the return value (if there is any) is correct.
        if (returnData.length > 0) {
            success = abi.decode(returnData, (bool));
        } else {
            // If there is no return value then a failure would have resulted in a revert
            success = true;
        }
    }

    // Special case for transactCall to support approvals on "bad" ERC20 tokens
    function transactTokenApprove(
        address wallet,
        address token,
        address spender,
        uint    amount
        )
        internal
        returns (bool success)
    {
        bytes memory txData = abi.encodeWithSelector(
            ERC20(0).approve.selector,
            spender,
            amount
        );
        bytes memory returnData = transactCall(wallet, token, 0, txData);
        // `transactCall` will revert if the call was unsuccessful.
        // The only extra check we have to do is verify if the return value (if there is any) is correct.
        if (returnData.length > 0) {
            success = abi.decode(returnData, (bool));
        } else {
            // If there is no return value then a failure would have resulted in a revert
            success = true;
        }
    }

    function transactDelegateCall(
        address wallet,
        address to,
        uint    value,
        bytes   memory data
        )
        internal
        returns (bytes memory)
    {
        return Wallet(wallet).transact(uint8(2), to, value, data);
    }

    function transactStaticCall(
        address wallet,
        address to,
        bytes   memory data
        )
        internal
        returns (bytes memory)
    {
        return Wallet(wallet).transact(uint8(3), to, 0, data);
    }
}
/// @title BaseModule
/// @dev This contract implements some common functions that are likely
///      be useful for all modules.
///
/// @author Daniel Wang - <daniel@loopring.org>
///
/// The design of this contract is inspired by Argent's contract codebase:
/// https://github.com/argentlabs/argent-contracts
function unbindMethods(address wallet)
    internal
{
    Wallet w = Wallet(wallet);
    bytes4[] memory methods = bindableMethods();
    for (uint i = 0; i < methods.length; i++) {
        w.bindMethod(methods[i], address(0));
    }
}

/// @dev Unbinds all methods from the given wallet.