contract RecoveryModule is SecurityModule
{
    event Recovered(
        address indexed wallet,
        address indexed oldOwner,
        address indexed newOwner,
        bool            removedAsGuardian
    );

    constructor(Controller _controller)
        public
        SecurityModule(_controller)
    {
    }

    /// @dev Recover a wallet by setting a new owner.
    /// @param wallet The wallet for which the recovery shall be cancelled.
    /// @param newOwner The new owner address to set.
    ///        The addresses must be sorted ascendently.
    function recover(
        address            wallet,
        address            newOwner
        )
        external
        nonReentrant
        notWalletOwner(wallet, newOwner)
        onlyFromMetaTx
        onlyHaveEnoughGuardians(wallet)
    {
        Wallet w = Wallet(wallet);
        address oldOwner = w.owner();
        require(newOwner != oldOwner, "SAME_ADDRESS");
        require(newOwner != address(0), "ZERO_ADDRESS");

        SecurityStore securityStore = controller.securityStore();
        bool removedAsGuardian = securityStore.isGuardianOrPendingAddition(wallet, newOwner);

        if (removedAsGuardian) {
           securityStore.removeGuardian(wallet, newOwner, now);
        }

        w.setOwner(newOwner);
        unlockWallet(wallet, true /*force*/);

        emit Recovered(wallet, oldOwner, newOwner, removedAsGuardian);
    }

    function verifySigners(
        address   wallet,
        bytes4    method,
        bytes     memory /*data*/,
        address[] memory signers
        )
        internal
        view
        override
        returns (bool)
    {
        require (method == this.recover.selector, "INVALID_METHOD");
        return GuardianUtils.requireMajority(
            controller.securityStore(),
            wallet,
            signers,
            GuardianUtils.SigRequirement.OwnerNotAllowed
        );
    }
}
/// @title RecoveryModule
function recover(
    address            wallet,
    address            newOwner
    )
    external
    nonReentrant
    notWalletOwner(wallet, newOwner)
    onlyFromMetaTx
    onlyHaveEnoughGuardians(wallet)
{
    Wallet w = Wallet(wallet);
    address oldOwner = w.owner();
    require(newOwner != oldOwner, "SAME_ADDRESS");
    require(newOwner != address(0), "ZERO_ADDRESS");

    SecurityStore securityStore = controller.securityStore();
    bool removedAsGuardian = securityStore.isGuardianOrPendingAddition(wallet, newOwner);

    if (removedAsGuardian) {
       securityStore.removeGuardian(wallet, newOwner, now);
    }

    w.setOwner(newOwner);
    unlockWallet(wallet, true /*force*/);

    emit Recovered(wallet, oldOwner, newOwner, removedAsGuardian);
}

/// @dev Recover a wallet by setting a new owner.
/// @param wallet The wallet for which the recovery shall be cancelled.
/// @param newOwner The new owner address to set.
///        The addresses must be sorted ascendently.