DexManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";

library Bytes32Library {

    // utility function to convert bytes32 to string
    function bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) {
        uint8 i = 0;
        while(i < 32 && _bytes32[i] != 0) {
            i++;
        }
        bytes memory bytesArray = new bytes(i);
        for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
            bytesArray[i] = _bytes32[i];
        }
        return string(bytesArray);
    }

}

library StringLibrary {

    // utility function to convert string to bytes32
    function stringToBytes32(string memory _string) internal pure returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(_string);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }
        assembly {
            result := mload(add(_string, 32))
        }
    }

}

interface IPortfolio {
    function pause() external;
    function unpause() external;
    function pauseDeposit(bool _paused) external;
    function updateTransferFeeRate(uint _rate, IPortfolio.Tx _rateType) external;
    function addToken(bytes32 _symbol, IERC20Upgradeable _token) external;
    function adjustAvailable(Tx _transaction, address _trader, bytes32 _symbol, uint _amount) external;
    function addExecution(ITradePairs.Order memory _maker, address _taker, bytes32 _baseSymbol, bytes32 _quoteSymbol,
                          uint _baseAmount, uint _quoteAmount, uint _makerfeeCharged,
                          uint _takerfeeCharged) external;
    function addAdmin(address _address) external;
    function removeAdmin(address _address) external;

    enum Tx  {WITHDRAW, DEPOSIT, EXECUTION, INCREASEAVAIL, DECREASEAVAIL}
    event PortfolioUpdated(Tx indexed transaction, address indexed wallet, bytes32 indexed symbol,
                           uint256 quantity, uint256 feeCharged, uint256 total, uint256 available, address refer);
}

interface ITradePairs {
    struct Order {
        bytes32 id;
        uint price;
        uint totalAmount;
        uint quantity;
        uint quantityFilled;
        uint totalFee;
        address traderaddress;
        Side side;
        Type1 type1;
        Status status;
    }

    function pause() external;
    function unpause() external;
    function pauseTradePair(bytes32 _tradePairId, bool _pairPaused) external;
    function pauseAddOrder(bytes32 _tradePairId, bool _allowAddOrder) external;
    function addTradePair(bytes32 _tradePairId, bytes32 _baseSymbol, uint8 _baseDecimals, uint8 _baseDisplayDecimals,
                          bytes32 _quoteSymbol, uint8 _quoteDecimals, uint8 _quoteDisplayDecimals,
                          uint _minTradeAmount, uint _maxTradeAmount) external;
    function getTradePairs() external view returns (bytes32[] memory);
    function setMinTradeAmount(bytes32 _tradePairId, uint _minTradeAmount) external;
    function getMinTradeAmount(bytes32 _tradePairId) external view returns (uint);
    function setMaxTradeAmount(bytes32 _tradePairId, uint _maxTradeAmount) external;
    function getMaxTradeAmount(bytes32 _tradePairId) external view returns (uint);
    function addOrderType(bytes32 _tradePairId, Type1 _type) external;
    function removeOrderType(bytes32 _tradePairId, Type1 _type) external;
    function setDisplayDecimals(bytes32 _tradePairId, uint8 _displayDecimals, bool _isBase) external;
    function getDisplayDecimals(bytes32 _tradePairId, bool _isBase) external view returns (uint8);
    function getDecimals(bytes32 _tradePairId, bool _isBase) external view returns (uint8);
    function getSymbol(bytes32 _tradePairId, bool _isBase) external view returns (bytes32);
    function updateRate(bytes32 _tradePairId, uint _rate, RateType _rateType) external;
    function getMakerRate(bytes32 _tradePairId) external view returns (uint);
    function getTakerRate(bytes32 _tradePairId) external view returns (uint);
    function setAllowedSlippagePercent(bytes32 _tradePairId, uint8 _allowedSlippagePercent) external;
    function getAllowedSlippagePercent(bytes32 _tradePairId) external view returns (uint8);
    function getNSellBook(bytes32 _tradePairId, uint nPrice, uint nOrder, uint lastPrice, bytes32 lastOrder) external view
                                                                    returns (uint[] memory, uint[] memory, uint , bytes32);
    function getNBuyBook(bytes32 _tradePairId, uint nPrice, uint nOrder, uint lastPrice, bytes32 lastOrder) external view
                                                                    returns (uint[] memory, uint[] memory, uint , bytes32);
    function getOrder(bytes32 _orderUid) external view returns (Order memory);
    function addOrder(bytes32 _tradePairId, uint _price, uint _quantity, Side _side, Type1 _type1) external;
    function cancelOrder(bytes32 _tradePairId, bytes32 _orderId) external;
    function cancelAllOrders(bytes32 _tradePairId, bytes32[] memory _orderIds) external;
    enum Side     {BUY, SELL}
    enum Type1    {MARKET, LIMIT, STOP, STOPLIMIT, LIMITFOK}
    enum Status   {NEW, REJECTED, PARTIAL, FILLED, CANCELED, EXPIRED, KILLED}
    enum RateType {MAKER, TAKER}
    enum Type2    {GTC, FOK}
}

contract DexOwner {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    // account collecting fees

    mapping(string=>bool) internal adminSettings;

    event ChangedSetAdmin(string setting, bool boolean);
    event RescueETH(address rescueAddress, uint256 amount);
    event RescueTokens(address token, address rescueAddress, uint256 amount);

    function setAdmin(string memory setting, bool boolean) external {
        adminSettings[setting] = boolean;
        emit ChangedSetAdmin(setting, boolean);
    }

    function rescueETH(address rescueAddress, uint256 amount) external payable {
        payable(rescueAddress).transfer(amount);
        emit RescueETH(rescueAddress, amount);
    }

    function rescueTokens(address token, address rescueAddress, uint256 amount) external payable {
        IERC20Upgradeable(token).safeTransfer(
            rescueAddress,
            amount
        );
        emit RescueTokens(token, rescueAddress, amount);
    }
}

contract DexManager is DexOwner, Initializable, AccessControlEnumerableUpgradeable {
    using SafeERC20Upgradeable for IERC20MetadataUpgradeable;
    using StringLibrary for string;
    using Bytes32Library for bytes32;

    // map and array of all trading pairs on DEXPOOLS
    ITradePairs private tradePairs;

    // portfolio reference
    IPortfolio private portfolio;

    event PortfolioSet(IPortfolio _oldPortfolio, IPortfolio _newPortfolio);
    event TradePairsSet(ITradePairs _oldTradePairs, ITradePairs _newTradePairs);

    function initialize() public initializer {
        __AccessControl_init();

        // intitialize the admins
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); // set deployment account to have DEFAULT_ADMIN_ROLE
        // initialize AVAX/USD price feed with fuji testnet contract,
        // heart-beat = 2m, decimals = 8
        adminSettings['deposits'] = true;
        adminSettings['withdraws'] = true;
        adminSettings['orders'] = true;
    }

    function owner() public view returns(address) {
        return getRoleMember(DEFAULT_ADMIN_ROLE, 0);
    }

    function addAdmin(address _address) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-01");
        grantRole(DEFAULT_ADMIN_ROLE, _address);
        portfolio.addAdmin(_address);
    }

    function removeAdmin(address _address) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-02");
        require(getRoleMemberCount(DEFAULT_ADMIN_ROLE)>1, "E-ALOA-01");
        revokeRole(DEFAULT_ADMIN_ROLE, _address);
        portfolio.removeAdmin(_address);
    }

    function isAdmin(address _address) public view returns(bool) {
        return hasRole(DEFAULT_ADMIN_ROLE, _address);
    }

    // FRONTEND FUNCTION TO GET A LIST OF TRADE PAIRS
    function getTradePairs() public view returns(bytes32[] memory) {
         return tradePairs.getTradePairs();
    }

     // DEPLOYMENT ACCOUNT FUNCTION TO UPDATE FEE RATES FOR DEPOSIT AND WITHDRAW
    function updateTransferFeeRate(uint _rate, IPortfolio.Tx _rateType) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-03");
        portfolio.updateTransferFeeRate(_rate, _rateType);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO UPDATE FEE RATE FOR EXECUTIONS
    function updateRate(bytes32 _tradePair, uint _rate, ITradePairs.RateType _rateType) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-04");
        tradePairs.updateRate(_tradePair, _rate, _rateType);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO UPDATE FEE RATE FOR EXECUTIONS
    function updateRates(bytes32 _tradePair, uint _makerRate, uint _takerRate) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-20");
        tradePairs.updateRate(_tradePair, _makerRate, ITradePairs.RateType.MAKER);
        tradePairs.updateRate(_tradePair, _takerRate, ITradePairs.RateType.TAKER);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO UPDATE ALL FEE RATES FOR EXECUTIONS
    function updateAllRates(uint _makerRate, uint _takerRate) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-21");
        bytes32[] memory pairs = getTradePairs();
        for (uint i=0; i<pairs.length; i++) {
            tradePairs.updateRate(pairs[i], _makerRate, ITradePairs.RateType.MAKER);
            tradePairs.updateRate(pairs[i], _takerRate, ITradePairs.RateType.TAKER);
        }
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO GET MAKER FEE RATE
    function getMakerRate(bytes32 _tradePairId) public view returns (uint) {
        return tradePairs.getMakerRate(_tradePairId);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO GET TAKER FEE RATE
    function getTakerRate(bytes32 _tradePairId) public view returns (uint) {
        return tradePairs.getTakerRate(_tradePairId);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO SET PORTFOLIO FOR THE EXCHANGE
    function setPortfolio(IPortfolio _portfolio) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-05");
        emit PortfolioSet(portfolio, _portfolio);
        portfolio = _portfolio;
    }

    // FRONTEND FUNCTION TO GET PORTFOLIO
    function getPortfolio() public view returns(IPortfolio) {
        return portfolio;
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO SET TRADEPAIRS FOR THE EXCHANGE
    function setTradePairs(ITradePairs _tradePairs) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-06");
        emit TradePairsSet(tradePairs, _tradePairs);
        tradePairs = _tradePairs;
    }

    // FRONTEND FUNCTION TO GET TRADEPAIRS
    function getTradePairsAddr() public view returns(ITradePairs) {
        return tradePairs;
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO ADD A NEW TRADEPAIR
    function addTradePair(bytes32 _tradePairId,
                          address _baseAssetAddr, uint8 _baseDisplayDecimals,
                          address _quoteAssetAddr, uint8 _quoteDisplayDecimals,
                          uint _minTradeAmount, uint _maxTradeAmount)
            public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-07");

        (bytes32 _baseAssetSymbol, uint8 _baseAssetDecimals) = getAssetMeta(_baseAssetAddr);
        (bytes32 _quoteAssetSymbol, uint8 _quoteAssetDecimals) = getAssetMeta(_quoteAssetAddr);
        // check if base asset is native AVAX, if not it is ERC20 and add it
        if (_baseAssetSymbol != bytes32("AVAX")) {
            //Only the base token can be an auction TOKEN
            portfolio.addToken(_baseAssetSymbol, IERC20MetadataUpgradeable(_baseAssetAddr));
        }
        // check if quote asset is native AVAX, if not it is ERC20 and add it
        if (_quoteAssetSymbol != bytes32("AVAX")) {
            portfolio.addToken(_quoteAssetSymbol, IERC20MetadataUpgradeable(_quoteAssetAddr));
        }
        tradePairs.addTradePair(_tradePairId,
                                _baseAssetSymbol, _baseAssetDecimals, _baseDisplayDecimals,
                                _quoteAssetSymbol, _quoteAssetDecimals, _quoteDisplayDecimals,
                                _minTradeAmount, _maxTradeAmount);
    }

    function getAssetMeta(address _assetAddr) private view returns (bytes32 _symbol, uint8 _decimals) {
        if (_assetAddr == address(0)) {
            return (bytes32("AVAX"), 18);
        } else {
            IERC20MetadataUpgradeable _asset = IERC20MetadataUpgradeable(_assetAddr);
            return (StringLibrary.stringToBytes32(_asset.symbol()), _asset.decimals());
        }
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO PAUSE AND UNPAUSE THE PORTFOLIO CONTRACT - AFFECTS ALL DEPOSIT AND WITHDRAW FUNCTIONS
    function pausePortfolio(bool _paused) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-08");
        if (_paused) {
            portfolio.pause();
        } else {
            portfolio.unpause();
        }
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO DISABLE ONLY DEPOSIT FUNCTIONS
    function pauseDeposit(bool _paused) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-09");
        portfolio.pauseDeposit(_paused);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO PAUSE AND UNPAUSE THE TRADEPAIRS CONTRACT
    // AFFECTS BOTH ADDORDER AND CANCELORDER FUNCTIONS FOR ALL TRADE PAIRS
    function pauseTrading(bool _tradingPaused) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-10");
        if (_tradingPaused) {
            tradePairs.pause();
        } else {
            tradePairs.unpause();
        }
    }

    function pauseForUpgrade(bool _paused) public {
        pausePortfolio(_paused);
        pauseTrading(_paused);
    }
        // DEPLOYMENT ACCOUNT FUNCTION TO PAUSE AND UNPAUSE THE TRADEPAIRS CONTRACT
    // AFFECTS BOTH ADDORDER AND CANCELORDER FUNCTIONS FOR A SELECTED TRADE PAIR
    function pauseTradePair(bytes32 _tradePairId, bool _tradePairPaused) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-11");
        tradePairs.pauseTradePair(_tradePairId, _tradePairPaused);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO DISABLE ONLY ADDORDER FUNCTION FOR A TRADEPAIR
    function pauseAddOrder(bytes32 _tradePairId, bool _addOrderPaused) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-12");
        tradePairs.pauseAddOrder(_tradePairId, _addOrderPaused);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO ADD AN ORDER TYPE TO A TRADEPAIR
    function addOrderType(bytes32 _tradePairId, ITradePairs.Type1 _type) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-13");
        tradePairs.addOrderType(_tradePairId, _type);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO REMOVE AN ORDER TYPE FROM A TRADEPAIR
    function removeOrderType(bytes32 _tradePairId, ITradePairs.Type1 _type) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-14");
        tradePairs.removeOrderType(_tradePairId, _type);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO SET MIN TRADE AMOUNT FOR A TRADEPAIR
    function setMinTradeAmount(bytes32 _tradePairId, uint _minTradeAmount) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-15");
        tradePairs.setMinTradeAmount(_tradePairId, _minTradeAmount);
    }

    // FRONTEND FUNCTION TO GET MIN TRADE AMOUNT
    function getMinTradeAmount(bytes32 _tradePairId) public view returns (uint) {
        return tradePairs.getMinTradeAmount(_tradePairId);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO SET MAX TRADE AMOUNT FOR A TRADEPAIR
    function setMaxTradeAmount(bytes32 _tradePairId, uint _maxTradeAmount) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-16");
        tradePairs.setMaxTradeAmount(_tradePairId, _maxTradeAmount);
    }

    // FRONTEND FUNCTION TO GET MAX TRADE AMOUNT
    function getMaxTradeAmount(bytes32 _tradePairId) public view returns (uint) {
        return tradePairs.getMaxTradeAmount(_tradePairId);
    }

    // FRONTEND FUNCTION TO SET DISPLAY DECIMALS
    function setDisplayDecimals(bytes32 _tradePairId, uint8 _displayDecimals, bool _isBase) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-17");
        tradePairs.setDisplayDecimals(_tradePairId, _displayDecimals, _isBase);
    }

    // FRONTEND FUNCTION TO GET DISPLAY DECIMALS
    function getDisplayDecimals(bytes32 _tradePairId, bool _isBase) public view returns(uint8) {
        return tradePairs.getDisplayDecimals(_tradePairId, _isBase);
    }

    // FRONTEND FUNCTION TO GET DECIMALS
    function getDecimals(bytes32 _tradePairId, bool _isBase) public view returns(uint8) {
         return tradePairs.getDecimals(_tradePairId, _isBase);
    }

    // FRONTEND FUNCTION TO GET DECIMALS
    function getSymbol(bytes32 _tradePairId, bool _isBase) public view returns(bytes32) {
         return tradePairs.getSymbol(_tradePairId, _isBase);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO SET ALLOWED SLIPPAGE PERCENT
    function setAllowedSlippagePercent(bytes32 _tradePairId, uint8 _allowedSlippagePercent) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-18");
        tradePairs.setAllowedSlippagePercent(_tradePairId, _allowedSlippagePercent);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO GET ALLOWED SLIPPAGE PERCENT
    function getAllowedSlippagePercent(bytes32 _tradePairId) public view returns (uint8) {
        return tradePairs.getAllowedSlippagePercent(_tradePairId);
    }

    // DEPLOYMENT ACCOUNT FUNCTION TO ADD A NEW TOKEN
    // NEEDS TO BE CALLED ONLY AFTER PORTFOLIO IS SET FOR EXCHANGE AND PORTFOLIO OWNERSHIP IS CHANGED TO EXCHANGE
    function addToken(bytes32 _symbol, IERC20Upgradeable _token) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "E-OACC-19");
        portfolio.addToken(_symbol, _token);
    }

    fallback() external {}

    // utility function to convert string to bytes32
    function stringToBytes32(string memory _string) public pure returns (bytes32 result) {
        return _string.stringToBytes32();
    }

    // utility function to convert bytes32 to string
    function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
        return _bytes32.bytes32ToString();
    }
}

Last updated