// File: contracts/BurnEngine.sol



// SPDX-License-Identifier: -- BCOM --

pragma solidity =0.8.21;

import "./OwnableMaster.sol";
import "./InterfaceHub/IVerseToken.sol";
import "./TransferHub/TransferHelper.sol";

error NoTokens();
error InvalidValue();

contract BurnEngine is OwnableMaster, TransferHelper {

    uint256 public burnCost;
    IVerseToken public immutable VERSE_TOKEN;

    event TokensBurned(
        address indexed burner,
        uint256 amount
    );

    event BurnCostUpdated(
        address indexed manager,
        uint256 newBurnCost
    );

    constructor(
        uint256 _burnCost,
        IVerseToken _verseToken
    )
        OwnableMaster(
            msg.sender
        )
    {
        burnCost = _burnCost;
        VERSE_TOKEN = _verseToken;
    }

    function adminBurn()
        external
        onlyMaster
    {
        _burn();
    }

    function userBurn()
        external
    {
        _safeTransferFrom(
            address(VERSE_TOKEN),
            msg.sender,
            address(this),
            burnCost
        );

        _burn();
    }

    function _burn()
        private
    {
        uint256 balance = VERSE_TOKEN.balanceOf(
            address(this)
        );

        if (balance == 0) {
            revert NoTokens();
        }

        VERSE_TOKEN.burn(
            balance
        );

        emit TokensBurned(
            msg.sender,
            balance
        );
    }

    function setBurnCost(
        uint256 _newBurnCost
    )
        external
        onlyMaster
    {
        if (_newBurnCost == 0) {
            revert InvalidValue();
        }

        burnCost = _newBurnCost;

        emit BurnCostUpdated(
            msg.sender,
            _newBurnCost
        );
    }
}

// File: contracts/TransferHub/TransferHelper.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.21;

import "./CallOptionalReturn.sol";

contract TransferHelper is CallOptionalReturn {

    bytes4 private constant transferSelector = IERC20
        .transfer
        .selector;

    bytes4 private constant transferFromSelector = IERC20
        .transferFrom
        .selector;

    /**
     * @dev
     * Allows to execute safe transfer for a token
     */
    function _safeTransfer(
        address _token,
        address _to,
        uint256 _value
    )
        internal
    {
        _callOptionalReturn(
            _token,
            abi.encodeWithSelector(
                transferSelector,
                _to,
                _value
            )
        );
    }

    /**
     * @dev
     * Allows to execute safe transferFrom for a token
     */
    function _safeTransferFrom(
        address _token,
        address _from,
        address _to,
        uint256 _value
    )
        internal
    {
        _callOptionalReturn(
            _token,
            abi.encodeWithSelector(
                transferFromSelector,
                _from,
                _to,
                _value
            )
        );
    }
}

// File: contracts/InterfaceHub/IVerseToken.sol



// SPDX-License-Identifier: -- BCOM --

pragma solidity =0.8.21;

import "./IERC20.sol";

interface IVerseToken is IERC20 {

    function burn(
        uint256 _value
    )
        external;
}

// File: contracts/OwnableMaster.sol



// SPDX-License-Identifier: -- BCOM --

pragma solidity =0.8.21;

error NoValue();
error NotMaster();
error NotProposed();

contract OwnableMaster {

    address public master;
    address public proposedMaster;

    address internal constant ZERO_ADDRESS = address(0x0);

    modifier onlyProposed() {
        _onlyProposed();
        _;
    }

    function _onlyMaster()
        private
        view
    {
        if (msg.sender == master) {
            return;
        }

        revert NotMaster();
    }

    modifier onlyMaster() {
        _onlyMaster();
        _;
    }

    function _onlyProposed()
        private
        view
    {
        if (msg.sender == proposedMaster) {
            return;
        }

        revert NotProposed();
    }

    event MasterProposed(
        address indexed proposer,
        address indexed proposedMaster
    );

    event ClaimedOwnership(
        address indexed newMaster,
        address indexed previousMaster
    );

    event RenouncedOwnership(
        address indexed previousMaster
    );

    constructor(
        address _master
    ) {
        if (_master == ZERO_ADDRESS) {
            revert NoValue();
        }
        master = _master;
    }

    /**
     * @dev Allows to propose next master.
     * Must be claimed by proposer.
     */
    function proposeOwner(
        address _proposedOwner
    )
        external
        onlyMaster
    {
        if (_proposedOwner == ZERO_ADDRESS) {
            revert NoValue();
        }

        proposedMaster = _proposedOwner;

        emit MasterProposed(
            msg.sender,
            _proposedOwner
        );
    }

    /**
     * @dev Allows to claim master role.
     * Must be called by proposer.
     */
    function claimOwnership()
        external
        onlyProposed
    {
        master = proposedMaster;

        emit ClaimedOwnership(
            master,
            msg.sender
        );
    }

    /**
     * @dev Removes master role.
     * No ability to be in control.
     */
    function renounceOwnership()
        external
        onlyMaster
    {
        master = ZERO_ADDRESS;
        proposedMaster = ZERO_ADDRESS;

        emit RenouncedOwnership(
            msg.sender
        );
    }
}

// File: contracts/TransferHub/CallOptionalReturn.sol



// SPDX-License-Identifier: -- BCOM --

pragma solidity =0.8.21;

import "../InterfaceHub/IERC20.sol";

error CallFailed();

contract CallOptionalReturn {

    /**
     * @dev
     * Helper function to do low-level call
     */
    function _callOptionalReturn(
        address token,
        bytes memory data
    )
        internal
        returns (bool call)
    {
        (
            bool success,
            bytes memory returndata
        ) = token.call(
            data
        );

        bool results = returndata.length == 0 || abi.decode(
            returndata,
            (bool)
        );

        call = success
            && results
            && token.code.length > 0;
    }
}
// File: contracts/InterfaceHub/IERC20.sol



// SPDX-License-Identifier: -- BCOM --

pragma solidity =0.8.21;

interface IERC20 {

    function totalSupply()
        external
        view
        returns (uint256);

    function balanceOf(
        address _account
    )
        external
        view
        returns (uint256);

    function transferFrom(
        address _sender,
        address _recipient,
        uint256 _amount
    )
        external
        returns (bool);

    function transfer(
        address _recipient,
        uint256 _amount
    )
        external
        returns (bool);

    function allowance(
        address owner,
        address spender
    )
        external
        view
        returns (uint256);

    function approve(
        address _spender,
        uint256 _amount
    )
        external
        returns (bool);

    function decimals()
        external
        view
        returns (uint8);

    event Transfer(
        address indexed from,
        address indexed to,
        uint256 value
    );

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    event Deposit(
        address indexed dst,
        uint wad
    );

    event Withdrawal(
        address indexed src,
        uint wad
    );
}