// File: Context.sol



// SPDX-License-Identifier: MIT

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: IRegistry.sol



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

interface IRegistry {
  event SystemWalletUpdated(address newWallet);
  event FeeVariablesChanged(uint256 indexed newFee, uint256 indexed newScale);
  event ContractStatusChanged(address indexed changed, bool indexed status);
  event CurrencyStatusChanged(address indexed changed, bool indexed status);

  function feeInfo(uint256 _salePrice) external view returns(address, uint256);
  function isPlatformContract(address toCheck) external view returns(bool);
  function isApprovedCurrency(address tokenContract) external view returns(bool);
  function setSystemWallet(address newWallet) external;
  function setFeeVariables(uint256 newFee, uint256 newScale) external;
  function setContractStatus(address toChange, bool status) external;
  function setCurrencyStatus(address tokenContract, bool status) external;
  function approveAllCurrencies() external;
}
// File: Ownable.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./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() {
        _setOwner(_msgSender());
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(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");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: Registry.sol



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

/**
 * @title Registry for the Etched platform
 * @author Linum Labs
 */

import "./Ownable.sol";
import "./IRegistry.sol";

contract EtchedRegistry is IRegistry, Ownable {
  mapping(address => bool) private platformContracts;
  mapping(address => bool) private approvedCurrencies;
  bool allowAllCurrencies;
  address systemWallet;
  // scale: how many zeroes should follow the fee
  // in the default values, there would be a 10% tax on a 18 decimal asset
  uint256 fee = 10_000;
  uint256 scale = 1e5;

  constructor() {
    approvedCurrencies[address(0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa)] = true;
  }

  /// @notice checks if a contract is active on the platform
  /// @dev used by platform contracts to verify interactions
  /// @param toCheck the address of the contract to check
  /// @return a boolean if the contract is active on the platform or not
  function isPlatformContract(address toCheck) external view override returns(bool) {
    return platformContracts[toCheck];
  }

  /// @notice checks if a token is approved for use on the platform
  /// @dev use 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa for ETH
  /// @param tokenContract the address of the token to check
  /// @return a boolean value if the token is approved for use on the platform
  function isApprovedCurrency(address tokenContract) external view override returns(bool) {
    if(allowAllCurrencies) return true;
    return approvedCurrencies[tokenContract];
  }

  /// @notice returns relevant details about system fees
  /// @dev structured similar to EIP2981 for royalties
  /// @param _salePrice the amount of a sale to calculate fees for
  /// @return a tuple of (address, uint256) of the system wallet and the amount of the fee
  function feeInfo(uint256 _salePrice) external view override returns(address, uint256) {
    return (systemWallet, (_salePrice * fee / scale));
  }

  /// @notice sets the address for the system wallet
  /// @param newWallet the address of the new system wallet
  function setSystemWallet(address newWallet) external override onlyOwner {
    systemWallet = newWallet;

    emit SystemWalletUpdated(newWallet);
  }

  /// @notice sets the global fee variables (fee and scale)
  /// @dev fee / scale = percentage
  /// @param newFee the new value for the fee global variable
  /// @param newScale the new value for the scale global variable
  function setFeeVariables(uint256 newFee, uint256 newScale) external override onlyOwner {
    fee = newFee;
    scale = newScale;
    emit FeeVariablesChanged(newFee, newScale);
  }

  /// @notice sets the status of a contract as active or inactive
  /// @param toChange the address of the contract to change the status of
  /// @param status a bool representing if the contract should be active or not
  function setContractStatus(address toChange, bool status) external override onlyOwner {
    string memory boolString = status == true ? "true" : "false";
    require(platformContracts[toChange] != status, 
      string(abi.encodePacked("contract status is already ", boolString))
    );
    platformContracts[toChange] = status;
    emit ContractStatusChanged(toChange, status);
  }

  /// @notice Explain to an end user what this does
  /// @dev Explain to a developer any extra details
  /// @param tokenContract the address of the token to change the status of
  /// @param status a bool representing if the token is approved or not
  function setCurrencyStatus(address tokenContract, bool status) external override onlyOwner {
    require(!allowAllCurrencies, "all currencies approved");
    string memory boolString = status == true ? "true" : "false";
    require(approvedCurrencies[tokenContract] != status, 
      string(abi.encodePacked("token status is already ", boolString))
    );
    approvedCurrencies[tokenContract] = status;
    emit CurrencyStatusChanged(tokenContract, status);
  }

  /// @notice globally allows all tokens
  /// @dev this is irrversible, and can only be called once
  function approveAllCurrencies() external override onlyOwner {
    require(!allowAllCurrencies, "already approved");
    allowAllCurrencies = true;
    emit CurrencyStatusChanged(address(0), true);
  }
}