def get_utxo(cls, address: str, amount: float): data = clove_req_json( f'{cls.blockcypher_url()}/addrs/{address}' '?limit=2000&unspentOnly=true&includeScript=true&confirmations=6') unspent = data.get('txrefs', []) for output in unspent: output['value'] = int(output['value']) unspent = sorted(unspent, key=lambda k: k['value'], reverse=True) utxo = [] total = 0 for output in unspent: value = from_base_units(output['value']) utxo.append( Utxo( tx_id=output['tx_hash'], vout=output['tx_output_n'], value=value, tx_script=output['script'], )) total += value if total > amount: return utxo logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)
def get_utxo(cls, address: str, amount: float): api_key = os.environ.get('CRYPTOID_API_KEY') if not api_key: raise ValueError('API key for cryptoid is required to get UTXOs.') data = clove_req_json(f'{cls.cryptoid_url()}/api.dws?q=unspent&key={api_key}&active={address}') unspent = data.get('unspent_outputs', []) for output in unspent: output['value'] = int(output['value']) unspent = sorted(unspent, key=lambda k: k['value'], reverse=True) utxo = [] total = 0 for output in unspent: value = from_base_units(output['value']) utxo.append( Utxo( tx_id=output['tx_hash'], vout=output['tx_ouput_n'], value=value, tx_script=output['script'], ) ) total += value if total > amount: return utxo logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)
def get_balance(cls, wallet_address: str) -> Optional[float]: data = clove_req_json( f'{cls.blockcypher_url()}/addrs/{wallet_address}/balance') if data is None: logger.error('Could not get details for address %s in %s network', wallet_address, cls.symbols[0]) return return from_base_units(data['balance'] or data['unconfirmed_balance'])
def __init__(self, network, contract: str, raw_transaction: Optional[str] = None, transaction_address: Optional[str] = None): if not raw_transaction and not transaction_address: raise ValueError( 'Provide raw_transaction or transaction_address argument.') self.network = network self.symbol = self.network.default_symbol self.contract = contract self.tx = None self.vout = None self.confirmations = None self.tx_address = transaction_address if raw_transaction: self.tx = self.network.deserialize_raw_transaction(raw_transaction) try: self.vout = self.tx.vout[0] except IndexError: raise ValueError('Given transaction has no outputs.') else: tx_json = self.network.get_transaction(transaction_address) if not tx_json: raise ValueError('No transaction found under given address.') self.vout = self.network.get_first_vout_from_tx_json(tx_json) self.confirmations = self.network.get_confirmations_from_tx_json( tx_json) if not self.vout: raise ValueError('Given transaction has no outputs.') contract_tx_out = self.vout contract_script = script.CScript.fromhex(self.contract) script_pub_key = contract_script.to_p2sh_scriptPubKey() valid_p2sh = script_pub_key == contract_tx_out.scriptPubKey self.address = str(CBitcoinAddress.from_scriptPubKey(script_pub_key)) try: self.balance = self.network.get_balance(self.address) except NotImplementedError: self.balance = None script_ops = list(contract_script) if valid_p2sh and self.is_valid_contract_script(script_ops): self.recipient_address = str( P2PKHBitcoinAddress.from_bytes(script_ops[6])) self.refund_address = str( P2PKHBitcoinAddress.from_bytes(script_ops[13])) self.locktime_timestamp = int.from_bytes(script_ops[8], byteorder='little') self.locktime = datetime.utcfromtimestamp(self.locktime_timestamp) self.secret_hash = b2x(script_ops[2]) self.value = from_base_units(contract_tx_out.nValue) else: raise ValueError('Given transaction is not a valid contract.')
def get_fee(cls) -> Optional[float]: '''Returns actual fee per kb.''' response = clove_req_json(cls.fee_endpoint) fee = response.get('high_fee_per_kb') if not fee: logger.error( 'Cannot find the right key (high_fee_per_kb) while getting fee in blockcypher.' ) return return from_base_units(fee)
def get_balance_blockcypher(network: str, address: str, testnet: bool) -> Optional[float]: subnet = 'test3' if testnet else 'main' url = f'https://api.blockcypher.com/v1/{network.lower()}/{subnet}/addrs/{address}/full?limit=2000' data = clove_req_json(url) if data is None: logger.debug('Could not get details for address %s in %s network', address, network) return return from_base_units(data['balance'])
def get_utxo(cls, address: str, amount: float) -> Optional[list]: ''' Getting list of UTXO objects. Args: address (str): wallet address to look for UTXO amount (float): minimum value that should be satisfied in UTXO objects Returns: list, None: list of UTXO objects or None it there was not enough UTXO Example: >>> from clove.network import Litecoin >>> network = Litecoin() >>> network.get_utxo(address='LUAn5PWmsPavgz32mGkqsUuAKncftS37Jq', amount=0.01) [ Utxo(tx_id='0cd90567497823097d03464b4b2d08dd659f1c5621dd55e9540bc9bcd3e191ec', vout='0', value='0.00976168', tx_script='76a91485c0522f6e23beb11cc3d066cd20ed732648a4e688ac', wallet=None, secret=None, refund=False), # noqa: E501 Utxo(tx_id='a5c027027c695f403fe570850e35ffd44bb28479ecaaee039372015fe0aae7b2', vout='0', value='0.00097114', tx_script='76a91485c0522f6e23beb11cc3d066cd20ed732648a4e688ac', wallet=None, secret=None, refund=False) # noqa: E501 ] ''' data = clove_req_json( f'{cls.blockcypher_url()}/addrs/{address}' '?limit=2000&unspentOnly=true&includeScript=true&confirmations=6') if not data: logger.debug( f'Cannot find UTXO for address {address} ({cls.symbols[0]})') return unspent = data.get('txrefs', []) for output in unspent: output['value'] = int(output['value']) unspent = sorted(unspent, key=lambda k: k['value'], reverse=True) utxo = [] total = 0 for output in unspent: value = from_base_units(output['value']) utxo.append( Utxo( tx_id=output['tx_hash'], vout=output['tx_output_n'], value=value, tx_script=output['script'], )) total += value if total > amount: return utxo logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)
def get_utxo_from_api(network: str, address: str, amount: float, use_blockcypher: bool = False, testnet: bool = False, cryptoid_api_key: str = None) -> Optional[list]: from clove.network.bitcoin.utxo import Utxo if use_blockcypher: subnet = 'test3' if testnet else 'main' api_url = f'https://api.blockcypher.com/v1/{network}/{subnet}/addrs/{address}' \ f'?limit=2000&unspentOnly=true&includeScript=true&confirmations=6' unspent_key = 'txrefs' vout_key = 'tx_output_n' elif cryptoid_api_key is None: raise ValueError('API key for cryptoid is required to get UTXOs.') else: api_url = f'https://chainz.cryptoid.info/{network}/api.dws?q=unspent&key={cryptoid_api_key}&active={address}' unspent_key = 'unspent_outputs' vout_key = 'tx_ouput_n' data = clove_req_json(api_url) if data is None: logger.debug('Could not get UTXOs for address %s in %s network', address, network) return unspent = data.get(unspent_key, []) for output in unspent: output['value'] = int(output['value']) unspent = sorted(unspent, key=lambda k: k['value'], reverse=True) utxo = [] total = 0 for output in unspent: value = from_base_units(output['value']) utxo.append( Utxo( tx_id=output['tx_hash'], vout=output[vout_key], value=value, tx_script=output['script'], )) total += value if total > amount: return utxo logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)
def get_balance(cls, wallet_address: str) -> float: ''' Returns wallet balance without unconfirmed transactions. Args: wallet_address (str): wallet address Returns: float: amount converted from base units Example: >>> from clove.network import Ravencoin >>> r = Ravencoin() >>> r.get_balance('RM7w75BcC21LzxRe62jy8JhFYykRedqu8k') >>> 18.99 ''' wallet_utxo = clove_req_json(f'{cls.api_url}/addr/{wallet_address}/balance') if not wallet_utxo: return 0 return from_base_units(wallet_utxo)
def get_fee(cls) -> Optional[float]: ''' Getting actual fee per kb Returns: float, None: actual fee per kb or None if eg. API is not responding Example: >>> from clove.network import BitcoinTestNet >>> network = BitcoinTestNet() >>> network.get_fee() 0.00024538 ''' response = clove_req_json(cls.blockcypher_url()) fee = response.get('high_fee_per_kb') if not fee: logger.error( 'Cannot find the right key (high_fee_per_kb) while getting fee in blockcypher.' ) return return from_base_units(fee)
def get_utxo(cls, address, amount): data = clove_req_json(f'{cls.api_url}/addrs/{address}/utxo') unspent = sorted(data, key=lambda k: k['satoshis'], reverse=True) utxo = [] total = 0 for output in unspent: value = from_base_units(output['satoshis']) utxo.append( Utxo( tx_id=output['txid'], vout=output['vout'], value=value, tx_script=output['scriptPubKey'], ) ) total += value if total > amount: return utxo logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)
def get_balance(cls, wallet_address: str) -> Optional[float]: ''' Returns wallet balance without unconfirmed transactions. Args: wallet_address (str): wallet address Returns: float, None: account balance converted from base units or None if something went wrong Example: >>> from clove.network import BitcoinTestNet >>> network = BitcoinTestNet() >>> network.get_balance('msJ2ucZ2NDhpVzsiNE5mGUFzqFDggjBVTM') 4.22188744 ''' data = clove_req_json( f'{cls.blockcypher_url()}/addrs/{wallet_address}/balance') if data is None: logger.error('Could not get details for address %s in %s network', wallet_address, cls.symbols[0]) return return from_base_units(data['balance'] or data['unconfirmed_balance'])
def get_utxo(cls, address, amount): data = clove_req_json( f'https://mona.chainseeker.info/api/v1/utxos/{address}') unspent = sorted(data, key=lambda k: k['value'], reverse=True) utxo = [] total = 0 for output in unspent: value = from_base_units(output['value']) utxo.append( Utxo( tx_id=output['txid'], vout=output['vout'], value=value, tx_script=output['scriptPubKey']['hex'], )) total += value if total > amount: return utxo logger.debug(f'Cannot find enough UTXO\'s. Found %.8f from %.8f.', total, amount)
def __init__( self, network, contract: str, raw_transaction: Optional[str]=None, transaction_address: Optional[str]=None ): if not raw_transaction and not transaction_address: raise ValueError('Provide raw_transaction or transaction_address argument.') self.network = network self.symbol = self.network.default_symbol self.contract = contract self.tx = None self.vout = None self.confirmations = None self.tx_address = transaction_address if raw_transaction: self.tx = self.network.deserialize_raw_transaction(raw_transaction) try: self.vout = self.tx.vout[0] except IndexError: raise ValueError('Given transaction has no outputs.') else: tx_json = get_transaction(network.default_symbol, transaction_address, network.is_test_network()) if not tx_json: raise ValueError('No transaction found under given address.') if 'hex' in tx_json: # transaction from blockcypher or raven explorer self.tx = self.network.deserialize_raw_transaction(tx_json['hex']) self.vout = self.tx.vout[0] else: # transaction from cryptoid incorrect_cscript = script.CScript.fromhex(tx_json['outputs'][0]['script']) correct_cscript = script.CScript([script.OP_HASH160, list(incorrect_cscript)[2], script.OP_EQUAL]) nValue = to_base_units(tx_json['outputs'][0]['amount']) self.vout = CTxOut(nValue, correct_cscript) if 'confirmations' in tx_json: self.confirmations = tx_json['confirmations'] elif 'block_height' in tx_json: self.confirmations = self.network.latest_block - tx_json['block_height'] elif 'block' in tx_json: self.confirmations = self.network.latest_block - tx_json['block'] if not self.vout: raise ValueError('Given transaction has no outputs.') contract_tx_out = self.vout contract_script = script.CScript.fromhex(self.contract) script_pub_key = contract_script.to_p2sh_scriptPubKey() valid_p2sh = script_pub_key == contract_tx_out.scriptPubKey self.address = str(CBitcoinAddress.from_scriptPubKey(script_pub_key)) self.balance = get_balance(self.network, self.address) script_ops = list(contract_script) if valid_p2sh and self.is_valid_contract_script(script_ops): self.recipient_address = str(P2PKHBitcoinAddress.from_bytes(script_ops[6])) self.refund_address = str(P2PKHBitcoinAddress.from_bytes(script_ops[13])) self.locktime_timestamp = int.from_bytes(script_ops[8], byteorder='little') self.locktime = datetime.utcfromtimestamp(self.locktime_timestamp) self.secret_hash = b2x(script_ops[2]) self.value = from_base_units(contract_tx_out.nValue) else: raise ValueError('Given transaction is not a valid contract.')
def test_from_base_units(satoshi_value): btc_value = from_base_units(satoshi_value) assert isinstance(btc_value, float) assert btc_value == satoshi_value / COIN
def get_balance(wallet_address: str) -> float: wallet_utxo = clove_req_json( f'https://mona.chainseeker.info/api/v1/utxos/{wallet_address}') if not wallet_utxo: return 0 return from_base_units(sum([utxo['value'] for utxo in wallet_utxo]))