// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import { BrokersStorage } from "./BrokersStorage.sol";
import { BrokersSignatureUtils } from "./BrokersSignatureUtils.sol";
import { BrokersTokenTransferrer } from "./BrokersTokenTransferrer.sol";
import { TradeInfo } from "../utils/DataTypes.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

/// @title NF3 OTC Broking Protocol
/// @author NF3 Exchange
/// @notice This contract inherits from IBroke interface.
/// @dev This contract is for brokers who are acting between users for trust trading.
/// @dev All trades between users are done by trusted third party(brokers) and brokers
/// @dev Get the fee from both sides whenever swapping assets.
contract Brokers is Ownable, BrokersTokenTransferrer, BrokersSignatureUtils {
    /// -----------------------------------------------------------------------
    /// Broker Actions
    /// -----------------------------------------------------------------------

    /// @notice Inherit from IBrokers
    function swap(
        TradeInfo calldata tradeInfo,
        bytes memory makerSignature,
        bool makerContra,
        bytes memory takerSignature,
        bool takerContra
    ) external override {
        // sanity checks on input
        _swapSanityChecks(tradeInfo, makerSignature, takerSignature);

        // Transfer maker fee if no contra
        if (!makerContra) {
            _transferFee(tradeInfo.maker, tradeInfo.makerFees);
        }

        // Transfer maker assets
        _transferAssets(
            tradeInfo.makerAssets,
            takerContra,
            tradeInfo.takerFees,
            tradeInfo.maker,
            tradeInfo.taker
        );

        // Transfer taker fee if no contract
        if (!takerContra) {
            _transferFee(tradeInfo.taker, tradeInfo.takerFees);
        }

        // Transfer taker assets
        _transferAssets(
            tradeInfo.takerAssets,
            makerContra,
            tradeInfo.makerFees,
            tradeInfo.taker,
            tradeInfo.maker
        );

        // Set both parties' nonce.
        _setNonce(tradeInfo.maker, tradeInfo.makerNonce);
        _setNonce(tradeInfo.taker, tradeInfo.takerNonce);

        emit BrokerSwapped(tradeInfo, _msgSender());
    }

    /// -----------------------------------------------------------------------
    /// Internal functions
    /// -----------------------------------------------------------------------

    /// @dev helper function to perform sanity checks while swapping assets
    function _swapSanityChecks(
        TradeInfo calldata _trade,
        bytes memory _makerSignature,
        bytes memory _takerSignature
    ) internal view {
        // verify signatures
        _verifyTradeInfoSignature(_trade, _makerSignature, _takerSignature);

        // check if called by the correct broker
        _checkBroker(
            _msgSender(),
            _trade.makerFees.broker,
            _trade.takerFees.broker
        );

        // check if nonces are correct
        _checkNonce(_trade.maker, _trade.makerNonce);
        _checkNonce(_trade.taker, _trade.takerNonce);

        // check if trade expired
        _checkExpiration(_trade.duration);
    }

    /// @dev Check if the nonce is in correct status.
    /// @param _owner Owner address
    /// @param _nonce Nonce value
    function _checkNonce(address _owner, uint256 _nonce) internal view {
        if (getNonce(_owner, _nonce)) {
            revert BrokersError(BrokersErrorCodes.INVALID_NONCE);
        }
    }

    /// @dev Check the asset expiration.
    /// @param _duration Trade expiration
    function _checkExpiration(uint256 _duration) internal view {
        if (_duration < block.timestamp) {
            revert BrokersError(BrokersErrorCodes.TIME_HAS_EXPIRED);
        }
    }

    /// @dev Check if the caller is valid broker who both parties chose.
    /// @param _caller Caller address
    /// @param _userBroker1 First user's broker
    /// @param _userBroker2 Second user's broker
    function _checkBroker(
        address _caller,
        address _userBroker1,
        address _userBroker2
    ) internal pure {
        if (_caller != _userBroker1 || _caller != _userBroker2) {
            revert BrokersError(BrokersErrorCodes.CALLER_IS_NOT_BROKER);
        }
    }
}
[{"inputs":[{"internalType":"enum BrokersSignatureUtils.BrokerSignatureUtilsErrorCodes","name":"code","type":"uint8"}],"name":"BrokerSignatureUtilsError","type":"error"},{"inputs":[{"internalType":"enum IBrokers.BrokersErrorCodes","name":"code","type":"uint8"}],"name":"BrokersError","type":"error"},{"anonymous":false,"inputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"indexed":false,"internalType":"struct Assets","name":"assets","type":"tuple"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"AssetsTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"broker","type":"address"}],"name":"BrokerRegistered","type":"event"},{"anonymous":false,"inputs":[{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"makerAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"takerAssets","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"broker","type":"address"},{"internalType":"address","name":"platform","type":"address"},{"internalType":"uint256","name":"brokerAmount","type":"uint256"},{"internalType":"uint256","name":"platformAmount","type":"uint256"}],"internalType":"struct Fees","name":"makerFees","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"broker","type":"address"},{"internalType":"address","name":"platform","type":"address"},{"internalType":"uint256","name":"brokerAmount","type":"uint256"},{"internalType":"uint256","name":"platformAmount","type":"uint256"}],"internalType":"struct Fees","name":"takerFees","type":"tuple"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"makerNonce","type":"uint256"},{"internalType":"uint256","name":"takerNonce","type":"uint256"}],"indexed":false,"internalType":"struct TradeInfo","name":"tradeInfo","type":"tuple"},{"indexed":true,"internalType":"address","name":"broker","type":"address"}],"name":"BrokerSwapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"broker","type":"address"},{"internalType":"address","name":"platform","type":"address"},{"internalType":"uint256","name":"brokerAmount","type":"uint256"},{"internalType":"uint256","name":"platformAmount","type":"uint256"}],"indexed":false,"internalType":"struct Fees","name":"fees","type":"tuple"}],"name":"FeeTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"NonceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"__nonce","type":"uint256"}],"name":"getNonce","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"makerAssets","type":"tuple"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum AssetType","name":"assetType","type":"uint8"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct AssetData[]","name":"assets","type":"tuple[]"}],"internalType":"struct Assets","name":"takerAssets","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"broker","type":"address"},{"internalType":"address","name":"platform","type":"address"},{"internalType":"uint256","name":"brokerAmount","type":"uint256"},{"internalType":"uint256","name":"platformAmount","type":"uint256"}],"internalType":"struct Fees","name":"makerFees","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"broker","type":"address"},{"internalType":"address","name":"platform","type":"address"},{"internalType":"uint256","name":"brokerAmount","type":"uint256"},{"internalType":"uint256","name":"platformAmount","type":"uint256"}],"internalType":"struct Fees","name":"takerFees","type":"tuple"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"makerNonce","type":"uint256"},{"internalType":"uint256","name":"takerNonce","type":"uint256"}],"internalType":"struct TradeInfo","name":"tradeInfo","type":"tuple"},{"internalType":"bytes","name":"makerSignature","type":"bytes"},{"internalType":"bool","name":"makerContra","type":"bool"},{"internalType":"bytes","name":"takerSignature","type":"bytes"},{"internalType":"bool","name":"takerContra","type":"bool"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]