// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: contracts/BonklerRescue.sol



// SPDX-License-Identifier: AGPL-3.0-only+VPL
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

/**
  An error emitted if we are about to interact with an unexpected
  implementation of the Bonkler proxy.
*/
error UnexpectedImplementation ();

/// A ProxyAdmin contract interface.
interface IProxyAdmin {
  function getProxyImplementation (
    address
  ) external view returns (address);
}

/// A Bonkler NFT contract interface.
interface IBonklerNFT {
  function setMinter (
    address
  ) external;
  function transferOwnership (
    address
  ) external;
}

/// A Bonkler auction proxy contract interface.
interface IBonklerAuction {
  function settleAuction () external;
}

/**
  @custom:benediction DEVS BENEDICAT ET PROTEGAT CONTRACTVS MEAM
  @title A contract for safely rescuing the proxy contract's
    trapped Bonkler.
  @author Tim Clancy <tim-clancy.eth>
  @custom:version 1.0

  This contract allows an owner to conditionally bundle Bonkler NFT mint
  delegation with auction settlement. This contract will only permit a 
  rescue attempt if the targeted Bonkler auction proxy has the expected
  implementation address.

  @custom:date October 4th, 2023
  @custom:terry "Is this too much voodoo for the next ten centuries?"
*/
contract BonklerRescue is Ownable {

  /// The address of the original Bonkler auction proxy contract.
  address public auction;

  /// The address of the Bonkler NFT contract.
  address public bonkler;

  /**
    Construct a migration contract targeting the original Bonkler auction
    and NFT contracts.

    @param _auction The address of the Bonkler auction contract to migrate from.
    @param _bonkler The address of the Bonkler NFT contract.
  */
  constructor (
    address _auction,
    address _bonkler
  ) {
    auction = _auction;
    bonkler = _bonkler;
  }

  /**
    Attempt a rescue; the rescue is only performed if there is no chance that
    the underlying proxy contract may have been maliciously upgraded.
    
    @param _intended If the Bonkler auction proxy implementation address
      matches this value, proceed with the rescue attempt.
  */
  function rescue (
    address _intended
  ) external onlyOwner {
    if (
      IProxyAdmin(
        0x6bCe3918a8E516fd4264a8e3f962ea0a3F2dEC99
      ).getProxyImplementation(
        0xF421391011Dc77c0C2489d384C26e915Efd9e2C5
      ) != _intended
    ) {
      revert UnexpectedImplementation();
    }
    IBonklerNFT(bonkler).setMinter(auction);
    IBonklerAuction(auction).settleAuction();
    IBonklerNFT(bonkler).setMinter(
      0x3033FDC27098D0006a72CcACCb850F3e7d700C00
    );
    IBonklerNFT(bonkler).transferOwnership(
      0xB520F068a908A1782a543aAcC3847ADB77A04778
    );
  }

  /**
    Allow the rightful owner of the Bonkler NFT to assume control.
  */
  function clawback () external {
    IBonklerNFT(bonkler).transferOwnership(
      0xB520F068a908A1782a543aAcC3847ADB77A04778
    );
  }
}