예제 #1
0
 def setWalletPriceFeederApi(self, accountName, wallet_api_url,
                             priceFeeder_contract_address):
     wallet_api = HXWalletApi(name='priceFeeder_service',
                              rpc_url=wallet_api_url)
     self.walletPriceFeederApi = PriceFeeder(accountName,
                                             priceFeeder_contract_address,
                                             wallet_api)
예제 #2
0
 def test_change_owner(self):
     previousOwner = self.pf.get_owner()
     newOwner = USER1['address']
     if previousOwner == PRICE_FEEDER['address']:
         self.pf.change_owner(USER1['address'])
     elif previousOwner == USER1['address']:
         newPf = PriceFeeder(USER1['account'], USER1['address'], self.api)
         newPf.change_owner(previousOwner)
         newOwner = USER1['address']
     time.sleep(6)
     currentOwner = self.pf.get_owner()
     assert (currentOwner != newOwner)
예제 #3
0
 def __init__(self, symbolPair, priceFeeder_contract_address, accountName,
              wallet_api_url, exchangeWebSites):
     self.logger = logging.getLogger("priceFeedingRobot")
     self.priceGrabs = []
     for websiteInfo in exchangeWebSites:
         self.priceGrabs.append(PriceGrab(symbolPair, websiteInfo))
     wallet_api = HXWalletApi(name='priceFeeder_service',
                              rpc_url=wallet_api_url)
     self.walletPriceFeederApi = PriceFeeder(accountName,
                                             priceFeeder_contract_address,
                                             wallet_api)
     account_addr = wallet_api.rpc_request("get_account_addr",
                                           [accountName])
     if (account_addr is None):
         raise RuntimeError("no account:" + accountName)
     self.account_addr = account_addr
     self.account = accountName
예제 #4
0
    def __init__(self, parent=None):
        super(PcmMainWindow, self).__init__(parent)
        self.setupUi(self)

        self.config_file = './pcm_config.json'
        try:
            with open(self.config_file, "r") as f:
                self.config = json.load(f)
        except:
            self.config = {
                'url_index': 0,
                'environment': 'test',
                'test': {
                    'collateral_contract': 'HXCSSGDHqaJDLto13BSZpAbrZoJf4RrGCtks',
                    'price_feeder_account': 'senator0',
                    'price_feeder_contract': 'HXCGba6bUaGeBtUQRGpHUePHVXzF1ygMAxR1',
                    'usd_contract': ''
                },
                'production': {
                    'collateral_contract': 'HXCKbMNLRv1X9wtwus9Wsnd6vkz6NporbZ5L',
                    'price_feeder_account': 'senator0',
                    'price_feeder_contract': 'HXCHfD5WiSKb57rU5BLqSbz3uHRXdBvsy19B',
                    'usd_contract': 'HXCTmGbEzqYq2LADmxQ3tDHnADp1yp5L36ih'
                }
            }
        self.priceFeederAccount = self.config[self.config['environment']]['price_feeder_account']
        self.walletUrlBox.setCurrentIndex(self.config['url_index'])
        self.currentApiUrl = self.walletUrlBox.currentText()
        self.collateral_contract = self.config[self.config['environment']]['collateral_contract']
        self.api = HXWalletApi(name='PCM', rpc_url=self.currentApiUrl)
        self.accounts = []
        self.initWidgets()
        self._refreshAccountList()
        self.priceFeeder = PriceFeeder(
            self.priceFeederAccount, \
            self.config[self.config['environment']]['price_feeder_contract'], \
            self.api)
        self.syncThread = DataSyncThread(self.api, self.collector, self.cdcOp)
        self.sinScanStop.connect(self.syncThread.stop)
        self.syncThread.sinSyncState.connect(self.syncStateChange)
        self.syncThread.start()
예제 #5
0
 def test_change_feeder(self):
     feeders = json.loads(self.pf.get_feeders())
     assert (PRICE_FEEDER['address'] in feeders)
     assert (USER1['address'] not in feeders)
     previousOwner = self.pf.get_owner()
     if previousOwner == PRICE_FEEDER['address']:
         currentPf = self.pf
     elif previousOwner == USER1['address']:
         newPf = PriceFeeder(USER1['account'], USER1['address'], self.api)
         currentPf = newPf
     currentPf.add_feeder(USER1['address'])
     time.sleep(6)
     feeders = json.loads(self.pf.get_feeders())
     assert (PRICE_FEEDER['address'] in feeders)
     assert (USER1['address'] in feeders)
     currentPf.remove_feeder(USER1['address'])
     time.sleep(6)
     feeders = json.loads(self.pf.get_feeders())
     assert (PRICE_FEEDER['address'] in feeders)
     assert (USER1['address'] not in feeders)
예제 #6
0
class PcmMainWindow(QMainWindow, Ui_MainWindow):
    sinScanStop = pyqtSignal()

    def __init__(self, parent=None):
        super(PcmMainWindow, self).__init__(parent)
        self.setupUi(self)

        self.config_file = './pcm_config.json'
        try:
            with open(self.config_file, "r") as f:
                self.config = json.load(f)
        except:
            self.config = {
                'url_index': 0,
                'environment': 'test',
                'test': {
                    'collateral_contract': 'HXCSSGDHqaJDLto13BSZpAbrZoJf4RrGCtks',
                    'price_feeder_account': 'senator0',
                    'price_feeder_contract': 'HXCGba6bUaGeBtUQRGpHUePHVXzF1ygMAxR1',
                    'usd_contract': ''
                },
                'production': {
                    'collateral_contract': 'HXCKbMNLRv1X9wtwus9Wsnd6vkz6NporbZ5L',
                    'price_feeder_account': 'senator0',
                    'price_feeder_contract': 'HXCHfD5WiSKb57rU5BLqSbz3uHRXdBvsy19B',
                    'usd_contract': 'HXCTmGbEzqYq2LADmxQ3tDHnADp1yp5L36ih'
                }
            }
        self.priceFeederAccount = self.config[self.config['environment']]['price_feeder_account']
        self.walletUrlBox.setCurrentIndex(self.config['url_index'])
        self.currentApiUrl = self.walletUrlBox.currentText()
        self.collateral_contract = self.config[self.config['environment']]['collateral_contract']
        self.api = HXWalletApi(name='PCM', rpc_url=self.currentApiUrl)
        self.accounts = []
        self.initWidgets()
        self._refreshAccountList()
        self.priceFeeder = PriceFeeder(
            self.priceFeederAccount, \
            self.config[self.config['environment']]['price_feeder_contract'], \
            self.api)
        self.syncThread = DataSyncThread(self.api, self.collector, self.cdcOp)
        self.sinScanStop.connect(self.syncThread.stop)
        self.syncThread.sinSyncState.connect(self.syncStateChange)
        self.syncThread.start()

    def initWidgets(self):
        self.btnRefresh.clicked.connect(self.refreshCdcs)
        self.btnChangeRatio.clicked.connect(lambda: self.cdcManagementAction(0))
        self.btnChangeFee.clicked.connect(lambda: self.cdcManagementAction(1))
        self.btnChangePenalty.clicked.connect(lambda: self.cdcManagementAction(2))
        self.btnChangeDiscount.clicked.connect(lambda: self.cdcManagementAction(3))
        self.btnChangePrice.clicked.connect(lambda: self.cdcManagementAction(4))
        self.btnOpenCdc.clicked.connect(self.openCdcDialog)
        self.cdcModel = QStandardItemModel(5,6)
        self.cdcModel.setHorizontalHeaderLabels(['CDC ID','BTC','HUSD', 'Stability Fee', 'state', 'block number'])
        self.tableView.setModel(self.cdcModel)
        self.tableView.doubleClicked.connect(lambda: self.existedCdcAction(0))
        self.btnPayback.clicked.connect(lambda: self.existedCdcAction(0))#'Payback'
        self.btnGenerate.clicked.connect(lambda: self.existedCdcAction(1))#'Generate'
        self.btnAdd.clicked.connect(lambda: self.existedCdcAction(2))#'AddCollateral'
        self.btnWithdraw.clicked.connect(lambda: self.existedCdcAction(3))#'Withdraw'
        self.btnLiquidate.clicked.connect(lambda: self.existedCdcAction(4))#'Liquidate'
        self.btnCloseCdc.clicked.connect(lambda: self.existedCdcAction(5))#'Close'
        self.collateralContractList.addItem(self.collateral_contract)
        self.walletUrlBox.currentIndexChanged.connect(self.changeUrl)

    def changeUrl(self):
        self.config['url_index'] = self.walletUrlBox.currentIndex()
        if self.config['url_index'] == 2:
            self.config['environment'] = 'production'
        else:
            self.config['environment'] = 'test'
        QMessageBox.information(self, 'Info', \
                        'URL changed. Restart Application to take effect.')
        

    def _refreshAccountList(self):
        self.accountList.clear()
        accounts = self.api.rpc_request('list_my_accounts', [])
        if accounts is None:
            return
        self.accounts = accounts
        self.collector = EventsCollector(self.accounts[0]['name'], self.collateral_contract, self.api)
        self.cdcOp = CDCOperation(self.accounts[0]['name'], self.collateral_contract, self.api)
        self.accountList.currentIndexChanged.connect(self.accountChange)
        self.priceFeederExist = False
        for a in self.accounts:
            if self.priceFeederAccount == a['name']:
                self.priceFeederExist = True
            self.accountList.addItem(a['name'])
        if not self.priceFeederExist:
            self.priceFeederAccount = self.accounts[0]['name']

    def cdcManagementAction(self, op):
        if not self.priceFeederExist:
            QMessageBox.warning(self, 'Warning', \
                        'No price feeder account in wallet.')
            return
        if op == 0:
            logging.debug('btnChangeRatio clicked')
            adminOp = CDCOperation(self.priceFeederAccount, self.collateral_contract, self.api)
            adminOp.set_liquidation_ratio(self.liquidationRatio.text())
        elif op == 1:
            logging.debug('btnChangeFee clicked')
            adminOp = CDCOperation(self.priceFeederAccount, self.collateral_contract, self.api)
            adminOp.set_annual_stability_fee(self.annualStabilityFee.text())
        elif op == 2:
            logging.debug('btnChangePenalty clicked')
            adminOp = CDCOperation(self.priceFeederAccount, self.collateral_contract, self.api)
            adminOp.set_liquidation_penalty(self.liquidationPenalty.text())
        elif op == 3:
            logging.debug('btnChangeDiscount clicked')
            adminOp = CDCOperation(self.priceFeederAccount, self.collateral_contract, self.api)
            adminOp.set_liquidation_discount(self.liquidationDiscount.text())
        elif op == 4:
            logging.debug('btnChangePrice clicked')
            self.priceFeeder.feed_price(self.currentPrice.text())
        else:
            logging.warning('unknown button is clicked')

    def openCdcDialog(self):
        dlg = OpenCdcDialog()
        dlg.openCdcSignal.connect(self.openCdcAction)
        dlg.exec_()

    def openCdcAction(self, arg):
        result = self.cdcOp.open_cdc(arg['btcAmount'], arg['usdAmount'])
        if result is not None and "trxid" in result:
            QMessageBox.information(self,"Success", "Open CDC success (%s)!" % result['trxid'])
        else:
            QMessageBox.warning(self,"Error", "Open CDC fail!")

    def existedCdcAction(self, action=0):
        r = self.tableView.currentIndex().row()
        if r < 0:
            QMessageBox.information(self, 'Info', 'Please select a CDC')
            return
        liquidateInfo = self.cdcOp.get_liquidable_info(self.cdcModel.data(self.cdcModel.index(r, 0)))
        if liquidateInfo is None:
            QMessageBox.information(self, 'Info', "Cannot get liquidation info, please retry later")
            return
        liquidateInfo = json.loads(liquidateInfo)
        if action == 4:
            if self.cdcModel.data(self.cdcModel.index(r, 4)) != 'OPEN':
                QMessageBox.information(self, 'Info', \
                    'The CDC (ID: %s) is [%s].' % (self.cdcModel.data(self.cdcModel.index(r, 0)), \
                        self.cdcModel.data(self.cdcModel.index(r, 4))))
            else:
                if not liquidateInfo['isNeedLiquidation'] or liquidateInfo['isBadDebt']:
                    QMessageBox.information(self, 'Info', \
                        'The CDC (ID: %s) cannot be liquidated.' % self.cdcModel.data(self.cdcModel.index(r, 0)))
            return
        data = {
            'cdc_id': self.cdcModel.data(self.cdcModel.index(r, 0)),
            'state': self.cdcModel.data(self.cdcModel.index(r, 4)),
            'available_usd': self.hUSDLineEdit.text(),
            'available_btc': self.bTCLineEdit.text(),
            'price': self.currentPrice.text(),
            'action': action,
            'liquidate': liquidateInfo
        }
        logging.debug(str(data))
        dlg = CDCOperationsDialog(args=data, parent=self)
        dlg.cdcOpSignal.connect(self.cdcTakeAction)
        dlg.exec_()

    def cdcTakeAction(self, arg):
        logging.debug(arg)
        cdcOp = CDCOperation(self.accountList.currentText(), self.collateral_contract, self.api)
        ret = ''
        if arg['action'] == 'Payback':
            ret = cdcOp.pay_back(arg['cdc_id'], arg['amount'])
        elif arg['action'] == 'Generate':
            ret = cdcOp.generate_stable_coin(arg['cdc_id'], arg['amount'])
        elif arg['action'] == 'AddCollateral':
            ret = cdcOp.add_collateral(arg['cdc_id'], arg['amount'])
        elif arg['action'] == 'Withdraw':
            ret = cdcOp.withdraw_collateral(arg['cdc_id'], arg['amount'])
        elif arg['action'] == 'Close':
            ret = cdcOp.close_cdc(arg['cdc_id'])
        elif arg['action'] == 'Liquidate':
            if arg['amount'] == '' or arg['amount2'] == '':
                QMessageBox.information(self, 'Info', \
                        'The CDC (ID: %s) cannot be liquidated.' % arg['cdc_id'])
                return
            self.cdcOp.liquidate(arg['cdc_id'], arg['amount'], arg['amount2'])
        else:
            pass
        if ret is None:
            retMessage = 'Fail to %s' % arg['action']
        else:
            retMessage = 'Success to %s' % arg['action']
        QMessageBox.information(self, 'Info', retMessage)
        logging.debug(ret)

    def closeEvent(self, e):
        self.sinScanStop.emit()
        self.syncThread.wait()
        with open(self.config_file, 'w') as wf:
            json.dump(self.config, wf)

    def accountChange(self, i):
        account = self.accounts[i]
        self.addressLineEdit.setText(account['addr'])
        balances = self.api.rpc_request('get_account_balances', [account['name']])
        account['balances'] = balances
        for b in account['balances']:
            if b['asset_id'] == '1.3.0':
                self.hXLineEdit.setText(convertCoinWithPrecision(b['amount'], 5))
            elif b['asset_id'] == '1.3.1':
                self.bTCLineEdit.setText(convertCoinWithPrecision(b['amount']))
        usdBalance = self.api.rpc_request('invoke_contract_offline', [account['name'], 'HXCcuGJV3cVnwMPk4S524ADcC9PWxRA3qKR2', 'balanceOf', account['addr']])
        self.hUSDLineEdit.setText(convertCoinWithPrecision(0 if usdBalance is None else usdBalance ))
        cdcs = self.collector.query_cdc_by_address(account['addr'])
        self.cdcModel.removeRows(0, self.cdcModel.rowCount())
        for r in range(len(cdcs)):
            self.cdcModel.setItem(r, 0, QStandardItem(cdcs[r].cdc_id))
            self.cdcModel.setItem(r, 1, QStandardItem(convertCoinWithPrecision(cdcs[r].collateral_amount)))
            self.cdcModel.setItem(r, 2, QStandardItem(convertCoinWithPrecision(cdcs[r].stable_token_amount)))
            logging.debug("----------------------"+str(cdcs[r].stable_token_amount))
            self.cdcModel.setItem(r, 3, QStandardItem('N/A'))
            if cdcs[r].state == 1:
                self.cdcModel.setItem(r, 4, QStandardItem('OPEN'))
                #FIXME, database is not updated. The CLOSED cdc query from chain will return None.
                cdcInfo = self.cdcOp.get_cdc(cdcs[r].cdc_id)
                if cdcInfo is None:
                    continue
                cdcInfo = json.loads(cdcInfo)
                self.cdcModel.setItem(r, 3, QStandardItem(convertCoinWithPrecision(cdcInfo['stabilityFee'])))
            elif cdcs[r].state == 2:
                self.cdcModel.setItem(r, 4, QStandardItem('LIQUIDATED'))
            elif cdcs[r].state == 3:
                self.cdcModel.setItem(r, 4, QStandardItem('CLOSED'))
            self.cdcModel.setItem(r, 5, QStandardItem(str(cdcs[r].block_number)))
        self.tableView.resizeColumnsToContents()
        self.tableView.resizeRowsToContents()
    
    def refreshCdcs(self):
        self.accountChange(self.accountList.currentIndex())

    def syncStateChange(self, stateChange):
        if stateChange['syncType'] == SYNC_STATE_TYPE:
            # self.syncLabel.setText(stateChange['data'])
            # self.syncLabel.adjustSize()
            self.statusBar().showMessage(stateChange['data'])
        elif stateChange['syncType'] == SYNC_CONTRACT_TYPE:
            self.cdcContractInfo = stateChange['data']
            self.collateralAdmin.setText(self.cdcContractInfo['admin'])
            self.collateralAsset.setText(self.cdcContractInfo['collateralAsset'])
            self.liquidationRatio.setText(self.cdcContractInfo['liquidationRatio'])
            self.annualStabilityFee.setText(self.cdcContractInfo['annualStabilityFee'])
            self.liquidationPenalty.setText(self.cdcContractInfo['liquidationPenalty'])
            self.liquidationDiscount.setText(self.cdcContractInfo['liquidationDiscount'])
            self.currentPrice.setText(self.priceFeeder.get_price())
예제 #7
0
class APriceFeeder:
    def __init__(self, symbolPair, priceFeeder_contract_address, accountName,
                 wallet_api_url, exchangeWebSites):
        self.logger = logging.getLogger("priceFeedingRobot")
        self.priceGrabs = []
        for websiteInfo in exchangeWebSites:
            self.priceGrabs.append(PriceGrab(symbolPair, websiteInfo))
        wallet_api = HXWalletApi(name='priceFeeder_service',
                                 rpc_url=wallet_api_url)
        self.walletPriceFeederApi = PriceFeeder(accountName,
                                                priceFeeder_contract_address,
                                                wallet_api)
        account_addr = wallet_api.rpc_request("get_account_addr",
                                              [accountName])
        if (account_addr is None):
            raise RuntimeError("no account:" + accountName)
        self.account_addr = account_addr
        self.account = accountName

    def setPriceGrab(self, exchangeWebSiteName, symbolPair):
        self.priceGrab = PriceGrab(exchangeWebSiteName, symbolPair)

    def setWalletPriceFeederApi(self, accountName, wallet_api_url,
                                priceFeeder_contract_address):
        wallet_api = HXWalletApi(name='priceFeeder_service',
                                 rpc_url=wallet_api_url)
        self.walletPriceFeederApi = PriceFeeder(accountName,
                                                priceFeeder_contract_address,
                                                wallet_api)

    def feedPrice(self):
        maxChangeRatio = 0.099999

        pricestr = None
        for priceGrab in self.priceGrabs:
            pricestr = priceGrab.grab_price()
            if (pricestr is not None):
                break
            else:
                self.logger.error("grab price fail ! from exchangeUrl:" +
                                  priceGrab.url)
        if (pricestr is None):
            self.logger.error(
                "grab price from all setted exchanges fail !!! please check network !!!"
            )
            return False

        r = self.walletPriceFeederApi.get_feedPrices()
        feedPrices = json.loads(r)
        origPriceStr = feedPrices[self.account_addr]
        price = float(pricestr)
        origPrice = float(origPriceStr)
        if (price > origPrice):
            while (price > origPrice * (1 + maxChangeRatio)):
                newPrice = origPrice * (1 + maxChangeRatio)
                r = self.walletPriceFeederApi.feed_price(str(newPrice))
                if (r is None):
                    return False
                self.logger.info(
                    " feeder:" + self.account +
                    "feed price exceed max change ratio 0.1  feed new price:" +
                    str(newPrice) + "orig price:" + str(origPrice) +
                    "\tcontract:" + self.walletPriceFeederApi.contract)
                origPrice = newPrice
            r = self.walletPriceFeederApi.feed_price(pricestr)
            if (r is None):
                return False
            self.logger.info(" feeder:" + self.account + "\tfeed price:" +
                             pricestr + "\tcontract:" +
                             self.walletPriceFeederApi.contract)

        else:
            while (price < origPrice * (1 - maxChangeRatio)):
                newPrice = origPrice * (1 - maxChangeRatio)
                r = self.walletPriceFeederApi.feed_price(str(newPrice))
                if (r is None):
                    return False
                self.logger.info(
                    " feeder:" + self.account +
                    "feed price exceed max change ratio 0.1  feed new price:" +
                    str(newPrice) + "orig price:" + str(origPrice) +
                    "\tcontract:" + self.walletPriceFeederApi.contract)
                origPrice = newPrice
            r = self.walletPriceFeederApi.feed_price(pricestr)
            if (r is None):
                return False
            self.logger.info(" feeder:" + self.account + "\tfeed price:" +
                             pricestr + "\tcontract:" +
                             self.walletPriceFeederApi.contract)
        return True
예제 #8
0
class TestPriceFeeder():
    def setup_method(self, function):
        self.api = HXWalletApi(name=function.__name__, rpc_url=HX_TESTNET_RPC)
        self.pf = PriceFeeder(PRICE_FEEDER['account'], FEEDER_CONTRACT_ID,
                              self.api)

    def teardown_function(self):
        self.api = None

    def test_get_state(self):
        owner = self.pf.get_state()
        assert (owner == 'COMMON')

    def test_get_base_asset(self):
        owner = self.pf.get_base_asset()
        assert (owner == 'BTC')

    def test_get_quote_asset(self):
        owner = self.pf.get_quote_asset()
        assert (owner == 'HXCcuGJV3cVnwMPk4S524ADcC9PWxRA3qKR2')

    def test_feed_price(self):
        previousPrice = self.pf.get_price()
        assert (previousPrice != None)
        previousPrice = decimal.Decimal(previousPrice)
        newPrice = previousPrice + decimal.Decimal('0.1')
        self.pf.feed_price(newPrice)
        time.sleep(6)
        currentPrice = self.pf.get_price()
        currentPrice = decimal.Decimal(currentPrice)
        assert (currentPrice == newPrice)
        self.pf.feed_price(previousPrice)

    def test_change_owner(self):
        previousOwner = self.pf.get_owner()
        newOwner = USER1['address']
        if previousOwner == PRICE_FEEDER['address']:
            self.pf.change_owner(USER1['address'])
        elif previousOwner == USER1['address']:
            newPf = PriceFeeder(USER1['account'], USER1['address'], self.api)
            newPf.change_owner(previousOwner)
            newOwner = USER1['address']
        time.sleep(6)
        currentOwner = self.pf.get_owner()
        assert (currentOwner != newOwner)

    def test_change_feeder(self):
        feeders = json.loads(self.pf.get_feeders())
        assert (PRICE_FEEDER['address'] in feeders)
        assert (USER1['address'] not in feeders)
        previousOwner = self.pf.get_owner()
        if previousOwner == PRICE_FEEDER['address']:
            currentPf = self.pf
        elif previousOwner == USER1['address']:
            newPf = PriceFeeder(USER1['account'], USER1['address'], self.api)
            currentPf = newPf
        currentPf.add_feeder(USER1['address'])
        time.sleep(6)
        feeders = json.loads(self.pf.get_feeders())
        assert (PRICE_FEEDER['address'] in feeders)
        assert (USER1['address'] in feeders)
        currentPf.remove_feeder(USER1['address'])
        time.sleep(6)
        feeders = json.loads(self.pf.get_feeders())
        assert (PRICE_FEEDER['address'] in feeders)
        assert (USER1['address'] not in feeders)
예제 #9
0
 def setup_method(self, function):
     self.api = HXWalletApi(name=function.__name__, rpc_url=HX_TESTNET_RPC)
     self.pf = PriceFeeder(PRICE_FEEDER['account'], FEEDER_CONTRACT_ID,
                           self.api)