コード例 #1
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())
コード例 #2
0
class TestHdaoEvents():
    @classmethod
    def setup_class(cls):
        pass

    @classmethod
    def teardown_class(cls):
        pass

    def setup_method(self, function):
        self.api = HXWalletApi(name="TestHdaoEvents", rpc_url=HX_TESTNET_RPC)
        self.cdcOp = CDCOperation(USER1['account'], CDC_CONTRACT_ID, self.api)
        self.collector = EventsCollector(USER1['account'], CDC_CONTRACT_ID,
                                         self.api)

    def teardown_function(self):
        self.api = None
        self.collector = None
        self.cdc = None

    # def test_collect(self):
    #     self.collector.collect_event(969234)

    def test_cdc_query(self):
        cdcs = self.collector.query_cdc_by_address(
            'HXNUeoaUVkUg9q2uokDwzdrxp1uE4L9VhTSs')
        assert (len(cdcs) >= 1)
        assert (cdcs[0].cdc_id == '0694d145b7000d8d5b13f9f4c4acbee3533ca14a')

    def test_cdc_op_query(self):
        cdcs = self.collector.query_cdc_op_by_id(
            'ee80006bb468a434af71c38cb62e1afac6a51c52')
        assert (len(cdcs) == 2)
        assert (cdcs[0].cdc_id == 'ee80006bb468a434af71c38cb62e1afac6a51c52')

    def test_cdc_normal_operations(self):
        info = self.api.rpc_request("info", [])
        block_num = info['head_block_num']
        result = self.cdcOp.open_cdc(0.001, 0.001)
        assert ("trxid" in result and result["trxid"] != "")
        confirmed = False
        cdc = None
        while not confirmed:
            time.sleep(3)
            self.collector.collect_event(block_num)
            cdcs = self.collector.query_cdc_by_address(USER1['address'])
            for c in cdcs:
                if c.block_number > block_num:
                    confirmed = True
                    cdc = c
                    break
        assert (cdc.collateral_amount == '100000'
                and cdc.collateral_amount == '100000')
        previousCdcId = cdc.cdc_id
        self.cdcOp.close_cdc(cdc.cdc_id)
        confirmed = False
        waitTimes = 0
        with pytest.raises(AssertionError):
            while waitTimes < 10:
                time.sleep(3)
                self.collector.collect_event(block_num)
                cdcs = self.collector.query_cdc_by_address(USER1['address'])
                for c in cdcs:
                    if c.cdc_id == previousCdcId:
                        if c.state == 3:
                            raise AssertionError
                        break