def balances(self, account=0): rsp = self.raw_request('getbalance', {'account_index': account}) retdata = { 'account_index' : 0, 'unlocked_balance': RoundDown( from_atomic( rsp['unlocked_balance'] ) ) , 'balance': RoundDown( from_atomic( rsp['balance'] ) ) , 'blocks_to_unlock' : rsp['blocks_to_unlock'] } return retdata
def import_key_images(self, key_images : list): rsp = self.raw_request('import_key_images', {'signed_key_images': key_images}) # rpc.import_key_images() unspent = RoundDown(Decimal(rsp['unspent']) * PICONERO) spent = RoundDown(Decimal(rsp['spent']) * PICONERO) #这里转为浮点数返回 retdata = { 'height': rsp['height'], 'unspent': unspent, 'spent': spent } return retdata
def GetBalanceInEther(self, addr): nBalance = self.eth_getBalance(addr, "latest") #向下取整, 而不是四舍五入, 四舍五入会导致金额偏大 dBalance = RoundDown(Decimal(str(nBalance)) / Decimal(str(10**18))) strBalance = '%.8f' % dBalance return strBalance
def get_single_token_balance(cls, rpcconn: USDPProxy, addr: str, token_name: str) -> dict: """ 获取 HTDF和token余额, 而不是获取所有 token余额 :param rpcconn: :param addr: :return: """ strhtdfbalance = USDP_GetBalance.get_balance(rpcconn, addr) strhtdfbalance = str(RoundDown(Decimal(strhtdfbalance))) if Decimal( strhtdfbalance) > Decimal('0.00000001') else '0.00000000' retdata = {'HTDF': strhtdfbalance} for contract_addr in HRC20_CONTRACT_MAP.keys(): token_info = HRC20_CONTRACT_MAP[contract_addr] if token_info['symbol'] != token_name.upper(): continue strbalance = rpcconn.getHRC20TokenBalance( contract_addr=contract_addr, address=addr) strsymbol = rpcconn.getHRC20_symbol(contract_addr=contract_addr) if strsymbol.upper() in g_special_token_list: strsymbol = 'HRC20-' + strsymbol retdata[strsymbol] = str(strbalance) break return retdata
def proccess(self, rpcconn): #1.从t_usdp_active_addr获取所有的地址 strSql = """SELECT address FROM t_het_active_addrs WHERE `balance` > 0.001 ORDER BY `balance` DESC LIMIT 100;""" sqlRet = sql.run(strSql) addrs = [] for item in sqlRet: if "address" in item: addrs.append(item["address"]) #2.实时获取所有地址的账户信息( addr, balance, account_no, sequence ) retData = [] for strAddr in addrs: retInfo = USDP_GetAccountInfo.account_info(rpcconn, strAddr) if not retInfo: #如果账户一毛钱不剩, 返回None strSql = """DELETE FROM t_het_active_addrs WHERE address='{0}';""".format( strAddr) logging.info("sql: {}".format(strSql)) sqlRet = sql.run(strSql) continue #防止HET 归集数量太大, 导致归集失败 if Decimal(retInfo['balance']) > Decimal('100000000.0'): retInfo['balance'] = '%.8f' % RoundDown( Decimal(retInfo['balance']) - Decimal('0.5432')) retData.append(retInfo) #对返回数据进行排序 sortedRet = sorted(retData, key=lambda keys: Decimal(keys['balance']), reverse=True) #3.返回数据 return sortedRet
def getHRC20TokenBalance(self, contract_addr: str, address: str) -> str: # 123.56.71.141:1317/hs/contract/htdf1nkkc48lfchy92ahg50akj2384v4yfqpm4hsq6y/70a0823100000000000000000000000067ee5cbe5cb9ae6794ca1437186f12066b0196af rlp_data = '70a08231' from htdf.my_bech32 import Bech32AddrToHex hex_addr = Bech32AddrToHex(addr=address) hex_addr_fmt = '0' * (32 * 2 - len(hex_addr)) + hex_addr rlp_data += hex_addr_fmt assert len(rlp_data) == (4 * 2 + 32 * 2), 'rlp data is illgal' urlpath = f'/hs/contract/{contract_addr}/{rlp_data}' rsp = self._get_url_call(urlpath) data = rsp.replace('"', '') assert len( data ) == 32 * 2, f'{contract_addr} data {data} is illegal, urlpath: {urlpath}' if int(data, 16) == 0: return '0.00000000' ndecimals = self.getHRC20_decimals(contract_addr=contract_addr) assert 1 < ndecimals <= 18 # balance = int(data, 16) * 1.0 / 10**(ndecimals) #会四舍五入!! 有问题 balance = Decimal(int(data, 16)) / Decimal(10**(ndecimals)) if balance < 0.00010000: return '0.00000000' # balance.quantize(Decimal("0.00000000"), getattr(decimal, 'ROUND_DOWN')) strfmt_balance = str(RoundDown(Decimal(str(balance)))) # strfmt_balance = '%.8f' % balance return strfmt_balance
def get_all_balance(cls, rpcconn, addr, symbol='', block='latest'): str_eth_balance = rpcconn.eth_getBalance(addr, block) dbalance = Decimal(str_eth_balance) / Decimal(10**18) dbalance = RoundDown(dbalance) retData = {} retData['ETC'] = "%.8f" % dbalance # if symbol.upper() == 'ETH': # pass # elif len(symbol) == 0: # # 检查代币余额 # for contract_addr in ERC20_CONTRACTS_MAP.values(): # strSymbol = rpcconn.eth_erc20_symbol(contract_addr) # strBalance = rpcconn.eth_erc20_balanceOf(contract_addr, addr, True) # retData[strSymbol] = strBalance # else: # contract_addr = ERC20_CONTRACTS_MAP[symbol] if symbol != 'ERC20-USDT' else ERC20_CONTRACTS_MAP['USDT'] # strSymbol = rpcconn.eth_erc20_symbol(contract_addr) # strBalance = rpcconn.eth_erc20_balanceOf(contract_addr, addr, True) # retData[strSymbol] = strBalance # # if 'USDT' in retData: # retData['ERC20-USDT'] = retData['USDT'] # del (retData['USDT']) return retData
def EncodeDecimal(o): if isinstance(o, decimal.Decimal): # return float(decimal.Decimal( round(o, 8))) d = RoundDown(o) f = float(d) return f # return float(o) raise TypeError(repr(o) + " is not JSON serializable")
def process(address: str): api = RippleProxy(host=XRP_RIPPLED_PUBLIC_API_URL, port=XRP_RIPPLED_PUBLIC_API_PORT) rsp = api.get_account_info(address=address) retinfo = {} retinfo['account'] = rsp['account_data']['Account'] retinfo['sequence'] = rsp['account_data']['Sequence'] balance = '%.8f' % RoundDown( Decimal(rsp['account_data']['Balance']) / Decimal(10**6)) # 防止假充值 retinfo['balance'] = balance return retinfo
def eth_erc20_balanceOf(self, contract_addr, user_addr, format=False): tmp_contract_addr = '0x' + contract_addr.replace('0x', '') data = '0x70a08231000000000000000000000000' + user_addr.replace( '0x', '') str_balance = self.eth_call(to_address=tmp_contract_addr, data=data) if not format: return hex(int(str_balance, 16))[2:].replace('L', '') else: nDecimals = self.eth_erc20_decimals(tmp_contract_addr) dBalance = Decimal(int(str_balance, 16)) / (10**nDecimals) d = RoundDown(dBalance) return '%.8f' % d
def get_all_balance(cls, rpcconn: USDPProxy, addr: str) -> dict: strhtdfbalance = USDP_GetBalance.get_balance(rpcconn, addr) strhtdfbalance = str(RoundDown(Decimal(strhtdfbalance))) if Decimal( strhtdfbalance) > Decimal('0.00000001') else '0.00000000' retdata = {'HTDF': strhtdfbalance} for contract_addr in HRC20_CONTRACT_MAP.keys(): strbalance = rpcconn.getHRC20TokenBalance( contract_addr=contract_addr, address=addr) strsymbol = rpcconn.getHRC20_symbol(contract_addr=contract_addr) if strsymbol.upper() in g_special_token_list: strsymbol = 'HRC20-' + strsymbol retdata[strsymbol] = str(strbalance) return retdata
def process(rpc_connection, blknumber, txindex): txdata = rpc_connection.eth_getTransactionByBlockNumberAndIndex( blknumber, txindex) blockData = rpc_connection.eth_getBlockByNumber(blknumber) txdata["blocktime"] = blockData[ "timestamp"] if blockData and "timestamp" in blockData else 0 txdata["confirmations"] = ETH_BlockNumber.latest( rpc_connection) - blknumber txdata["blockNumber"] = blknumber from utils import filtered, alterkeyname retData = filtered(alterkeyname(txdata, 'hash', 'txid'), [ "confirmations", "blocktime", "blockNumber", "nonce", "txid", "from", "to", "value", "gas", "gasPrice" ]) if txdata else False for key in ["nonce", "gas", "value", "gasPrice", "blocktime"]: if "0x" in retData[key]: retData[key] = str(int(retData[key], 16)) getcontext().prec = 30 dValue = RoundDown(Decimal(retData[key]) / Decimal(10**18)) if key in ["value"]: retData[key] = "%.8f" % dValue return retData
def GetTransactionsInfoFromBlock(self, nHeight): try: txRet = [] nChainLastest = self.GetLastestBlockNumberFromBlockChain() if not nChainLastest: return txRet blockInfo = self.eth_getBlockByNumber(nHeight) if not blockInfo: return txRet #使用Bloom Filter 判断是否包含ERC20交易 # bf = hex2Dec( blockInfo['logsBloom'] ) # included_contracts = [] #测试合约地址是否存在 # if bloom_query(bf, hexstr_to_bytes(ERC20_TRANSFER_EVENT_HASH)): #检查 transfer事件是否存在 # for contract_addr in ERC20_CONTRACTS_LIST: # con_addr = contract_addr.replace('0x', '').lower() #如果包含'0x'则去掉 '0x' # if bloom_query(bf, hexstr_to_bytes(con_addr) ): # included_contracts.append(con_addr) nTimestamp = hex2Dec(blockInfo["timestamp"]) nNumber = hex2Dec(blockInfo["number"]) nGasLimit = hex2Dec(blockInfo["gasLimit"]) nConfirmations = nChainLastest - nNumber for tx in blockInfo["transactions"]: txTmp = {} #如果是创建合约, to是 null if tx['to'] is None: continue if tx['to'] in self.exUserAddrs: #普通的ETC充币 # 普通的ETC转账 txTmp["txid"] = tx["hash"] txTmp["from"] = tx["from"] txTmp["to"] = tx["to"] txTmp["nonce"] = hex2Dec(tx["nonce"]) txTmp["blocktime"] = nTimestamp txTmp["confirmations"] = nConfirmations txTmp["blockNumber"] = nNumber txTmp['symbol'] = 'ETC' getcontext().prec = 30 txTmp["value"] = "%.8f" % RoundDown( Decimal(hex2Dec(tx["value"])) / Decimal(10**18)) # ether print("found tx:{}".format(txTmp)) txRet.append(txTmp) else: #有可能是ERC20代币充值 # if 0 == len(included_contracts): # continue # # #如果是合约调用合约进行的ERC20代币转账, to地址可能并不是代币合约的地址, # #因此必须通过bloomFilter进行过滤, 防止漏掉充值 # # receipt = self.eth_getTransactionReceipt(tx['hash']) # if int(receipt['status'], 16) != 1: continue # if len(receipt['logs']) < 1: continue # # bf = hex2Dec(receipt['logsBloom']) # if not bloom_query(bf, unhexlify(ERC20_TRANSFER_EVENT_HASH)): # 检查 transfer事件是否存在 # # print("transfer event is not in logsBloom.") # continue # # tmp_con_addrs = [] # for contract_addr in ERC20_CONTRACTS_LIST: # con_addr = contract_addr.replace('0x', '').lower() # 如果包含'0x'则去掉 '0x' # if bloom_query(bf, hexstr_to_bytes( con_addr)): # tmp_con_addrs.append('0x' + con_addr) # break # # if len(tmp_con_addrs) == 0: # continue # print("tmp include contract addr : {}".format(tmp_con_addrs)) # # # 支持合约的批量转账 # for log in receipt['logs']: # if log['removed']: continue # topics = log['topics'] # # # transfer事件的topics的数量必须是3 # if len(topics) != 3: # continue # # # 如果合约地址不是交易所要监控的合约,则跳过 # if log['address'] not in tmp_con_addrs: # continue # # # 如果事件的哈希不是transfer的, 则跳过 # event_hash = topics[0] # if ERC20_TRANSFER_EVENT_HASH.replace('0x', '').lower() != event_hash.replace('0x', '').lower(): # continue # # # addr_from 并不完全等于 tx['from'], 即合约的调用者并一定是token的发送方, # # 可以参考ERC20标准的 transferFrom 方法 # addr_from = '0x' + topics[1][-40:] # ERC20代币的发送方 # addr_to = '0x' + topics[2][-40:] # ERC20代币的接收方 # # #如果from地址是交易所的地址(一般是归集操作), 则也需要更新活跃地址表 # if addr_from in self.exUserAddrs: # strSql = """INSERT INTO t_eth_patch_addrs(`address`) VALUES('{}')""".format(addr_from) # sqlRet = sql.run(strSql) # # if addr_to not in self.exUserAddrs: # print('{} is not exchange address'.format(addr_to)) # continue # # # # 获取代币简称 , 必须用 log['address'], 不能用tx['to'], # # 因为合约调用合约, tx['to']是调用者, log['address']才是真正执行的合约 # strSymbol = self.eth_erc20_symbol(log['address']) # nDecimal = self.eth_erc20_decimals(log['address']) # 获取小数位数 # strValue = log['data'] # # txTmp["txid"] = tx["hash"] # txTmp["from"] = addr_from # txTmp["to"] = addr_to.strip() # txTmp["nonce"] = hex2Dec(tx["nonce"]) # txTmp["blocktime"] = nTimestamp # txTmp["confirmations"] = nConfirmations # txTmp["blockNumber"] = nNumber # txTmp['symbol'] = strSymbol # # getcontext().prec = 30 # txTmp["value"] = "%.8f" % RoundDown(Decimal(hex2Dec(strValue)) / Decimal(10 ** nDecimal)) # 单位转换 # print("found tx: {}".format(txTmp)) # # txRet.append(txTmp) pass return txRet except Exception as e: print("GetTransactionsInfoFromBlock(nHeight) error:", e) return None pass
def get_deposit_txs(self): ret_txs = [] params = { 'in': True, 'pending': False, 'failed': False, 'pool': False, 'filter_by_height': False, #通过高度过滤, 暂时不通过高度过滤, 后期充币比较多的时候,再根据高度过滤 # 'min_height' : 0, # 'max_height': 0, 'account_index': 0, # 'subaddr_indices' : [] # 如果为空, 则查询所有 } logging.info(f'starting get_transfers') res = self.raw_request(method='get_transfers', params=params) logging.info(f'get_transfers finished. result: {res}') res_in_txs = res['in'] if 'in' in res else [] for in_tx in res_in_txs: tmp_tx = {} tmp_tx['dst_addr'] = in_tx['address'] tmp_tx['amount'] = RoundDown( Decimal(in_tx['amount']) / Decimal(10**12)) tmp_tx['confirmations'] = in_tx['confirmations'] if in_tx['confirmations'] < 10: logging.info( f'txid:{in_tx["txid"]} confirmations : {in_tx["confirmations"]}/10 .' ) continue tmp_tx['blocknumber'] = in_tx['height'] #subaddr_index {major, minor} 可以推出 和 in_tx['address']相同 tmp_tx['timestamp'] = in_tx['timestamp'] tmp_tx['txid'] = in_tx['txid'] tmp_tx['type'] = in_tx['type'] #充币交易必须是 'in' #double_spend_seen 一般是用于 pool 交易的判断, 这里只是 if in_tx['double_spend_seen']: logging.warning(f'txid:{tmp_tx["txid"]} is double spend tx ') continue tmp_tx['unlock_time'] = in_tx[ 'unlock_time'] # 必须是 0, 如果不是0, 说明还没解锁 tmp_tx['locked'] = in_tx['locked'] # 如果是锁定的不要,充币不能锁定 if tmp_tx['locked']: logging.warning( f'txid:{tmp_tx["txid"]} is locked tx, unlock_time: { tmp_tx["unlock_time"] }' ) continue major = in_tx['subaddr_index']['major'] minor = in_tx['subaddr_index']['minor'] if major == 0 and minor == 0: logging.warning( f'txid:{tmp_tx["txid"]} is master addr(0, 0) tx , do not regard as deposit tx ' ) continue subaddr = get_address_ex(private_view_key=XMR_PRIV_VIEW_KEY, master_addr=XMR_MASTER_ADDR, major=major, minor=minor) if subaddr != tmp_tx['dst_addr']: logging.info( f'tx dst_addr is {tmp_tx["dst_addr"]}, but ({major}, {minor}) sub addr is {subaddr}' ) continue ret_txs.append(tmp_tx) return ret_txs
def __GetTransactionFromBlock(self, nBlockNum: int): data = self.getBlockByBlockNum(nBlockNum) timeStr = data["block_meta"]["header"]["time"] timeStr = timeStr[:timeStr.rfind('.')] ta = time.strptime(timeStr, "%Y-%m-%dT%H:%M:%S") timestamp = int(time.mktime(ta)) #print("timestamp", timestamp) retData = [] txs = data["block"]["txs"] if not isinstance(txs, list): return [] for tx in txs: txData = {} #2019-06-13 yqq 因失败的交易也会被打包进区块, 所以,加上交易有效性判断 strTxid = str(tx["Hash"]).strip() if len(strTxid) != 64: print("strTxid is invalid txid") continue #2020-04-14 增加HRC20交易有效性判断 if not self.isValidTx(strTxid): print("%s is invalid tx" % strTxid) continue txData["txid"] = tx["Hash"] txData["from"] = tx["From"] txData["to"] = tx["To"] txData["amount"] = tx["Amount"][0]["amount"] #单位是 usdp, 不用再除10**8 txData["timestamp"] = timestamp strTxid = txData["txid"].strip() strFrom = txData["from"].strip() strTo = txData["to"].strip() #print("self.__exUserAddrs size:{}".format(len(self.__exUserAddrs))) strLog = "{} is valid tx, from: {} to:{}".format( strTxid, strFrom, strTo) if strFrom in self.__exUserAddrs: #这种情况是地址被归集了,需要更新余额 self.__RefreshBalanceIntoDB(strFrom) if strTo not in self.__exUserAddrs: #仅监测交易所用户的地址, #2020-04-13 增加HRC20判断 if self.__strCoinType.lower() != 'htdf': strLog = strLog + ", but not for exchange." print(strLog) continue if strTo not in HRC20_CONTRACT_MAP: strLog = strLog + ", but not for exchange." print(strLog) continue #如果是关心的 HRC20 合约交易 data = tx['Data'] # 函数签名(4字节) + 代币接收地址(32字节) + 代币金额(32字节) if len(data) != (4 + 32 + 32) * 2: print('data length ') continue method_sig = data[:4 * 2] if method_sig.lower() != 'a9059cbb': print( f'method sig is not `transfer` sig . data:{method_sig}' ) continue token_recipient = data[4 * 2 + 12 * 2:4 * 2 + 32 * 2] #只去最后 20 字节, 去掉全面填补的 0 recipient_bech32_addr = HexAddrToBech32( hrp='htdf', hexstraddr=token_recipient) token_amount = data[4 * 2 + 32 * 2:] token_decimal = HRC20_CONTRACT_MAP[strTo]['decimal'] token_symbol = HRC20_CONTRACT_MAP[strTo]['symbol'] if recipient_bech32_addr not in self.__exUserAddrs: print( f'token_recipient bech32_addr {recipient_bech32_addr} is not belong to exchange' ) #如果源地址是交易所的, 更新 HRC20 代币代币余额 if strFrom in self.__exUserAddrs: self.__RefreshHRC20TokenBalance( contract_addr=strFrom, address=recipient_bech32_addr, symbol=token_symbol) continue #以防万一 if not (1 < token_decimal <= 18): print( f' token_decimal is invalid ! {strTo} : {token_decimal} ' ) continue amount = Decimal(int(token_amount, 16)) / Decimal( 10**token_decimal) if amount < 0.000000001: print( f' token_amount {token_amount} is too small! skip it! ' ) continue strfmt_amount = str(RoundDown(amount)) strsql = """INSERT INTO tb_hrc20_deposit(`txid`,`symbol`,`from`,`to`,`value`,`block_number`,`block_time`,`confirmations`) """ strsql += f"""VALUES('{tx["Hash"]}', '{token_symbol}', '{strFrom}', '{recipient_bech32_addr}', '{strfmt_amount}', {nBlockNum}, {timestamp}, 10) """ strsql += """ ON DUPLICATE KEY UPDATE `confirmations`={}; """.format( 10) print(strsql) sqlret = sql.run(strsql) self.__RefreshHRC20TokenBalance(contract_addr=strTo, address=recipient_bech32_addr, symbol=token_symbol) continue else: # 正常充币的情况 strLog = strLog + ", it's for exchange." self.__RefreshBalanceIntoDB(strTo) print(strLog) retData.append(txData) return retData