Exemplo n.º 1
0
    def publish(self, raw_transaction: str) -> Optional[str]:
        '''
        Method for publishing transactions.

        Args:
            raw_transaction (str): hex string containing signed transaction

        Returns:
            str, None: transaction address or None if transaction wasn't published

        Example:
            >>> raw_transaction = '010000000184a38a4e8743964249665fb241fbd3...35b'
            >>> network.publish(raw_transaction)
            70eefae0106b787e592e12914e4040efd8181dd299fa314d8f66da6a95cd1cfe
        '''
        for attempt in range(1, TRANSACTION_BROADCASTING_MAX_ATTEMPTS + 1):
            transaction_address = self.broadcast_transaction(raw_transaction)

            if transaction_address is None:
                logger.warning(
                    'Transaction broadcast attempt no. %s failed. Retrying...',
                    attempt)
                continue

            logger.info(
                'Transaction broadcast is successful. End of broadcasting process.'
            )
            return transaction_address

        logger.warning(
            '%s attempts to broadcast transaction failed. Broadcasting process terminates!',
            TRANSACTION_BROADCASTING_MAX_ATTEMPTS)
Exemplo n.º 2
0
 def get_token_by_symbol(cls, symbol: str):
     """  Get raw_transaction by encoding Transaction object token by provided symbol """
     token = cls.get_token_by_attribute('symbol', symbol)
     if not token:
         logger.warning(f'No token found for symbol {symbol}')
         return
     return cls.token_class.from_namedtuple(token)
Exemplo n.º 3
0
    def sign_raw_transaction(cls, raw_transaction: str,
                             private_key: str) -> str:
        '''
        Method to sign raw transactions.

        Args:
            raw_transaction (str): raw transaction hex string
            private_key (str): private key hex string

        Returns:
            str: signed transaction hex string

        Raises:
            ValueError: if given private key is invalid

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> raw_transaction = '0xf8f28201f4843b9aca008302251694ce07ab9477bc20790b88b398a2a9e0f626c7d26387b1a2bc2ec50000b8c47337c993000000000000000000000000000000000000000000000000000000005bd564819d3e84874c199ca4656d434060ec1a393750ab74000000000000000000000000000000000000000000000000d867f293ba129629a9f9355fa285b8d3711a9092000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080'  # noqa: E501
            >>> network.sign_raw_transaction(raw_transaction, MY_PRIVATE_KEY)
            '0xf901318201f4843b9aca008302251694ce07ab9477bc20790b88b398a2a9e0f626c7d26387b1a2bc2ec50000b8c47337c993000000000000000000000000000000000000000000000000000000005bd564819d3e84874c199ca4656d434060ec1a393750ab74000000000000000000000000000000000000000000000000d867f293ba129629a9f9355fa285b8d3711a90920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ca0d1c5b984ef2629eeb7c96f48a645566b2caf4130b0f3d7060ad5225946eee9e99f9928c5dfe868b45efbb9f8ae7d64d6162591c78961439c49e836947842e178'  # noqa: E501
        '''
        transaction = cls.deserialize_raw_transaction(raw_transaction)

        try:
            transaction.sign(private_key)
            logger.debug("Transaction signed")
        except Exception:
            logger.warning(
                "Invalid private key. Transaction could not be signed.")
            raise ValueError('Invalid private key.')

        return cls.get_raw_transaction(transaction)
Exemplo n.º 4
0
    def approve_token(
        self,
        sender_address: str,
        value: Union[str, Decimal],
        token_address: str=None,
    ) -> EthereumTokenApprovalTransaction:

        if not isinstance(value, Decimal):
            value = Decimal(str(value))

        token = None
        if token_address:
            token = self.get_token_by_address(token_address)
            if not token:
                logger.warning('Unknown ethereum token')
                raise ValueError('Unknown token')

        transaction = EthereumTokenApprovalTransaction(
            self,
            sender_address,
            value,
            token,
        )

        return transaction
Exemplo n.º 5
0
    def deserialize_raw_transaction(raw_transaction: str) -> Transaction:
        '''
        Deserializing raw transaction and returning Transaction object

        Args:
            raw_transaction (str): raw transaction hex string

        Returns:
            `ethereum.transactions.Transaction`: Ethereum transaction object

        Raises:
            ImpossibleDeserialization: if the raw transaction was not deserializable

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> transaction = network.deserialize_raw_transaction('0xf8f28201f4843b9aca008302251694ce07ab9477bc20790b88b398a2a9e0f626c7d26387b1a2bc2ec50000b8c47337c993000000000000000000000000000000000000000000000000000000005bd564819d3e84874c199ca4656d434060ec1a393750ab74000000000000000000000000000000000000000000000000d867f293ba129629a9f9355fa285b8d3711a9092000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808080')  # noqa: E501
            <Transaction(821b)>
        '''
        try:
            transaction = rlp.hex_decode(raw_transaction, Transaction)
            logger.debug('Deserialization succeed')
        except (ValueError, RLPException):
            logger.warning(f'Deserialization with {raw_transaction} failed')
            raise ImpossibleDeserialization()

        transaction._cached_rlp = None
        transaction.make_mutable()

        return transaction
Exemplo n.º 6
0
    def publish(self, transaction: Union[str, Transaction]) -> Optional[str]:
        '''
        Method to publish transaction

        Args:
            transaction (str, `ethereum.transactions.Transaction`): signed transaction

        Returns:
            str, None: transaction hash or None if something goes wrong

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> signed_transaction = '0xf901318201f4843b9aca008302251694ce07ab9477bc20790b88b398a2a9e0f626c7d26387b1a2bc2ec50000b8c47337c993000000000000000000000000000000000000000000000000000000005bd564819d3e84874c199ca4656d434060ec1a393750ab74000000000000000000000000000000000000000000000000d867f293ba129629a9f9355fa285b8d3711a90920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ca0d1c5b984ef2629eeb7c96f48a645566b2caf4130b0f3d7060ad5225946eee9e99f9928c5dfe868b45efbb9f8ae7d64d6162591c78961439c49e836947842e178'  # noqa: E501
            >>> network.publish(signed_transaction)
            '0x4fd41289b816f6122e59a0759bd10441ead75d550562f4b3aad2fddc56eb3274'
        '''
        raw_transaction = transaction if isinstance(
            transaction, str) else self.get_raw_transaction(transaction)
        try:
            published_transaction = self.web3.eth.sendRawTransaction(
                raw_transaction).hex()
            logger.debug(
                f'Transaction {published_transaction} published successful')
            return published_transaction
        except ValueError:
            logger.warning(f'Unable to publish transaction {raw_transaction}')
            return
Exemplo n.º 7
0
 def get_token_by_address(self, address: str):
     token = self.get_token_by_attribute(
         'address', address) or self.get_token_from_token_contract(address)
     if not token:
         logger.warning(f'No token found for address {address}')
         return
     return EthToken.from_namedtuple(token)
Exemplo n.º 8
0
    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
        '''
        try:
            # This endpoint is available from v0.3.1
            fee = clove_req_json(
                f'{cls.api_url}/utils/estimatefee?nbBlocks=1')['1']
        except (TypeError, KeyError):
            logger.error(
                f'Incorrect response from API when getting fee from {cls.api_url}/utils/estimatefee?nbBlocks=1'
            )
            return cls._calculate_fee()

        if fee == -1:
            logger.debug(f'Incorrect value in estimatedFee: {fee}')
            return cls._calculate_fee()
        fee = float(fee)
        if fee > 0:
            logger.warning(
                f'Got fee = 0 for ({cls.symbols[0]}), calculating manually')
            return fee
        return cls._calculate_fee()
Exemplo n.º 9
0
    def get_method_name(self, method_id: str) -> str:
        '''
        Returning Atomic Swap method name based on method identifier.

        Args:
            method_id (str): method identifier

        Returns:
            str: method name

        Raises:
            UnsupportedTransactionType: if method identifier is not recognized

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> network.get_method_name('7337c993')
            'initiate'
        '''
        try:
            return {
                self.initiate: 'initiate',
                self.redeem: 'redeem',
                self.refund: 'refund',
            }[method_id]
        except KeyError:
            logger.warning(f'Unrecognized method id {self.method_id}')
            raise UnsupportedTransactionType(
                f'Unrecognized method id {self.method_id}')
Exemplo n.º 10
0
    def refund(self):
        contract = self.network.web3.eth.contract(address=self.contract_address, abi=self.network.abi)

        if self.locktime > datetime.utcnow():
            locktime_string = self.locktime.strftime('%Y-%m-%d %H:%M:%S')
            logger.warning(f"This contract is still valid! It can't be refunded until {locktime_string} UTC.")
            raise RuntimeError(f"This contract is still valid! It can't be refunded until {locktime_string} UTC.")

        refund_func = contract.functions.refund(self.secret_hash, self.recipient_address)
        tx_dict = {
            'nonce': self.network.web3.eth.getTransactionCount(self.refund_address),
            'value': 0,
            'gas': ETH_REFUND_GAS_LIMIT,
        }

        tx_dict = refund_func.buildTransaction(tx_dict)

        transaction = EthereumTokenTransaction(network=self.network)
        transaction.tx = Transaction(
            nonce=tx_dict['nonce'],
            gasprice=tx_dict['gasPrice'],
            startgas=tx_dict['gas'],
            to=tx_dict['to'],
            value=tx_dict['value'],
            data=Web3.toBytes(hexstr=tx_dict['data']),
        )
        transaction.value = self.value
        transaction.token = self.token
        transaction.recipient_address = self.refund_address
        logger.debug('Transaction refunded')
        return transaction
Exemplo n.º 11
0
    def atomic_swap(
        self,
        sender_address: str,
        recipient_address: str,
        value: Union[str, Decimal],
        secret_hash: str=None,
        token_address: str=None,
    ) -> EthereumAtomicSwapTransaction:
        """ Return EthereumAtomicSwapTransaction object,
            which initiate and build transaction beetwen sender and recipient """

        if not isinstance(value, Decimal):
            value = Decimal(str(value))

        token = None
        if token_address:
            token = self.get_token_by_address(token_address)
            if not token:
                logger.warning('Unknown ethereum token')
                raise ValueError('Unknown token')

        transaction = EthereumAtomicSwapTransaction(
            self,
            sender_address,
            recipient_address,
            value,
            secret_hash,
            token,
        )
        return transaction
Exemplo n.º 12
0
 def publish(self, transaction: Union[str, Transaction]) -> Optional[str]:
     """ Method to publish transaction """
     raw_transaction = transaction if isinstance(transaction, str) else self.get_raw_transaction(transaction)
     try:
         published_transaction = self.web3.eth.sendRawTransaction(raw_transaction).hex()
         logger.debug(f'Transaction {published_transaction} published successful')
         return published_transaction
     except ValueError:
         logger.warning(f'Unable to publish transaction {raw_transaction}')
         return
Exemplo n.º 13
0
 def get_method_name(self, method_id):
     try:
         return {
             self.initiate: 'initiate',
             self.initiate_token: 'initiate',
             self.redeem: 'redeem',
             self.refund: 'refund',
         }[method_id]
     except KeyError:
         logger.warning(f'Unrecognized method id {self.method_id}')
         raise UnsupportedTransactionType(f'Unrecognized method id {self.method_id}')
Exemplo n.º 14
0
    def sign_raw_transaction(cls, raw_transaction: str, private_key: str) -> str:
        """ Method to sign raw transactions """
        transaction = cls.deserialize_raw_transaction(raw_transaction)

        try:
            transaction.sign(private_key)
            logger.debug("Transaction signed")
        except Exception:
            logger.warning("Invalid private key. Transaction could not be signed.")
            raise ValueError('Invalid private key.')

        return cls.get_raw_transaction(transaction)
Exemplo n.º 15
0
    def web3_provider_address(self) -> str:
        '''
        Returns:
            str: address for web3 provider.

        Raises:
            ValueError: if the `INFURA_TOKEN` environment variable is not set.
        '''
        token = os.environ.get('INFURA_TOKEN')
        if not token:
            logger.warning('INFURA_TOKEN environment variable was not set.')
            raise ValueError('INFURA_TOKEN environment variable was not set.')
        return f'https://{self.infura_network}.infura.io/{token}'
Exemplo n.º 16
0
 def get_fee(cls) -> float:
     """Ravencoin has a different endpoint for fee (estimatesmartfee, not estimatefee)"""
     try:
         fee = clove_req_json(f'{cls.api_url}/utils/estimatesmartfee?nbBlocks=1')['1']
     except (TypeError, KeyError):
         logger.error(
             f'Incorrect response from API when getting fee from {cls.api_url}/utils/estimatefee?nbBlocks=1'
         )
         return cls._calculate_fee()
     if fee > 0:
         return fee
     logger.warning(f'({cls.symbols[0]}) Got fee = 0, calculating manually')
     return cls._calculate_fee()
Exemplo n.º 17
0
    def atomic_swap(
        self,
        sender_address: str,
        recipient_address: str,
        value: Union[str, Decimal],
        secret_hash: str = None,
        token_address: str = None,
    ) -> EthereumAtomicSwapTransaction:
        '''
        Return EthereumAtomicSwapTransaction object, which initiate and build transaction between sender and recipient.

        Args:
            sender_address (str): wallet address of the sender
            recipient_address (str): wallet address of the recipient
            value (str, Decimal): amount to swap
            secret_hash (str): optional secret hash to be used in transaction. If None then the new hash
                will be generated.
            token_address: address of the ERC20 token contract to swap

        Returns:
            EthereumAtomicSwapTransaction: atomic swap unsigned transaction for Ethereum

        Raises:
            ValueError: if you use an incorrect token address

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> network.atomic_swap('0x999F348959E611F1E9eab2927c21E88E48e6Ef45', '0xd867f293Ba129629a9f9355fa285B8D3711a9092', '0.05')  # noqa: E501
            <clove.network.ethereum.transaction.EthereumAtomicSwapTransaction at 0x7f286d16dba8>
        '''

        if not isinstance(value, Decimal):
            value = Decimal(str(value))

        token = None
        if token_address:
            token = self.get_token_by_address(token_address)
            if not token:
                logger.warning('Unknown ethereum token')
                raise ValueError('Unknown token')

        transaction = EthereumAtomicSwapTransaction(
            self,
            sender_address,
            recipient_address,
            value,
            secret_hash,
            token,
        )
        return transaction
Exemplo n.º 18
0
def clove_req(url: str) -> Optional[HTTPResponse]:
    """Make a request with Clove user-agent header"""
    req = urllib.request.Request(url, headers={'User-Agent': 'Clove'})
    try:
        request_start = time.time()
        logger.debug('  Requesting: %s', url)
        resp = urllib.request.urlopen(req)
        response_time = time.time() - request_start
        logger.debug('Got response: %s [%.2fs]', url, response_time)
    except (HTTPError, URLError) as e:
        logger.warning('Could not open url %s', url)
        logger.exception(e)
        return
    return resp
Exemplo n.º 19
0
    def deserialize_raw_transaction(raw_transaction: str) -> Optional[Transaction]:
        """ Method to deserialize raw method.
        It's deserializing raw_transaction and returns Transaction object"""
        try:
            transaction = rlp.hex_decode(raw_transaction, Transaction)
            logger.debug('Deserialization succeed')
        except (ValueError, RLPException):
            logger.warning(f'Deserialization with {raw_transaction} failed')
            raise ImpossibleDeserialization()

        transaction._cached_rlp = None
        transaction.make_mutable()

        return transaction
Exemplo n.º 20
0
    def refund(self) -> EthereumTokenTransaction:
        '''
        Creates transaction that can refund a contract.

        Returns:
            EthereumTokenTransaction: unsigned transaction object with refund transaction

        Raises:
            RuntimeError: if contract is still valid
            ValueError: if contract balance is 0
        '''
        if self.balance == 0:
            raise ValueError("Balance of this contract is 0.")
        contract = self.contract

        if self.locktime > datetime.utcnow():
            locktime_string = self.locktime.strftime('%Y-%m-%d %H:%M:%S')
            logger.warning(
                f"This contract is still valid! It can't be refunded until {locktime_string} UTC."
            )
            raise RuntimeError(
                f"This contract is still valid! It can't be refunded until {locktime_string} UTC."
            )

        refund_func = contract.functions.refund(self.secret_hash,
                                                self.recipient_address)
        tx_dict = {
            'nonce':
            self.network.web3.eth.getTransactionCount(self.refund_address),
            'value': 0,
            'gas': ETH_REFUND_GAS_LIMIT,
        }

        tx_dict = refund_func.buildTransaction(tx_dict)

        transaction = EthereumTokenTransaction(network=self.network)
        transaction.tx = Transaction(
            nonce=tx_dict['nonce'],
            gasprice=tx_dict['gasPrice'],
            startgas=tx_dict['gas'],
            to=tx_dict['to'],
            value=tx_dict['value'],
            data=Web3.toBytes(hexstr=tx_dict['data']),
        )
        transaction.value = self.value
        transaction.token = self.token
        transaction.recipient_address = self.refund_address
        logger.debug('Transaction refunded')
        return transaction
Exemplo n.º 21
0
 def get_token_from_token_contract(self, token_address: str) -> Optional[Token]:
     """ Getting information from token contract and creating Token.
         Smart contract is taken based on provided address """
     token_address = self.unify_address(token_address)
     token_contract = self.web3.eth.contract(address=token_address, abi=ERC20_BASIC_ABI)
     concise = ConciseContract(token_contract)
     try:
         name = concise.name()
         symbol = concise.symbol()
         decimals = concise.decimals()
         logger.debug(f'Token get from contract with success')
     except (OverflowError, BadFunctionCallOutput):
         logger.warning(f'Unable to take token from address: {token_address}')
         return
     return Token(name, symbol, token_address, decimals)
Exemplo n.º 22
0
    def update_blacklist(self, node):
        '''
        Increasing number of failed connection attempts to a give node.

        Args:
            node (str): node's IP address or domain

        Returns:
            None
        '''
        try:
            self.blacklist_nodes[node] += 1
        except KeyError:
            logger.warning('Unable to update  blacklist')
            self.blacklist_nodes[node] = 1
Exemplo n.º 23
0
    def __init__(self, network, tx_dict):

        self.network = network
        self.tx_dict = tx_dict
        self.method_id = self.network.extract_method_id(tx_dict['input'])
        self.type = self.network.get_method_name(self.method_id)
        self.token = None

        if not self.is_initiate:
            logger.warning('Not a contract transaction.')
            raise ValueError('Not a contract transaction.')

        if self.is_token_contract:
            self.abi = self.network.token_abi
        else:
            self.abi = self.network.abi

        input_types = get_abi_input_types(
            find_matching_fn_abi(self.abi, fn_identifier=self.type))
        input_names = get_abi_input_names(
            find_matching_fn_abi(self.abi, fn_identifier=self.type))
        input_values = decode_abi(
            input_types, Web3.toBytes(hexstr=self.tx_dict['input'][10:]))
        self.inputs = dict(zip(input_names, input_values))

        self.locktime = datetime.utcfromtimestamp(self.inputs['_expiration'])
        self.recipient_address = Web3.toChecksumAddress(
            self.inputs['_participant'])
        self.refund_address = self.tx_dict['from']
        self.secret_hash = self.inputs['_hash'].hex()
        self.contract_address = Web3.toChecksumAddress(self.tx_dict['to'])
        self.confirmations = self.network.latest_block - self.tx_dict[
            'blockNumber']

        if self.is_token_contract:
            self.value_base_units = self.inputs['_value']
            self.token_address = Web3.toChecksumAddress(self.inputs['_token'])
            self.token = self.network.get_token_by_address(self.token_address)
            self.value = self.token.value_from_base_units(
                self.value_base_units)
            self.symbol = self.token.symbol
        else:
            self.value_base_units = self.tx_dict['value']
            self.value = self.network.value_from_base_units(
                self.value_base_units)
            self.symbol = self.network.default_symbol
Exemplo n.º 24
0
    def publish(self, raw_transaction: str):
        for attempt in range(1, TRANSACTION_BROADCASTING_MAX_ATTEMPTS + 1):
            transaction_address = self.broadcast_transaction(raw_transaction)

            if transaction_address is None:
                logger.warning(
                    'Transaction broadcast attempt no. %s failed. Retrying...',
                    attempt)
                continue

            logger.info(
                'Transaction broadcast is successful. End of broadcasting process.'
            )
            return transaction_address

        logger.warning(
            '%s attempts to broadcast transaction failed. Broadcasting process terminates!',
            TRANSACTION_BROADCASTING_MAX_ATTEMPTS)
Exemplo n.º 25
0
    def get_fee(cls) -> Optional[float]:
        # This endpoint is available from v0.3.1
        try:
            fee = clove_req_json(f'{cls.api_url}/utils/estimatefee?nbBlocks=1')['1']
        except (TypeError, KeyError):
            logger.error(
                f'Incorrect response from API when getting fee from {cls.api_url}/utils/estimatefee?nbBlocks=1'
            )
            return cls._calculate_fee()

        if fee == -1:
            logger.debug(f'Incorrect value in estimatedFee: {fee}')
            return cls._calculate_fee()
        fee = float(fee)
        if fee > 0:
            logger.warning(f'Got fee = 0 for ({cls.symbols[0]}), calculating manually')
            return fee
        return cls._calculate_fee()
Exemplo n.º 26
0
    def approve_token(
        self,
        sender_address: str,
        value: Union[str, Decimal],
        token_address: str = None,
    ) -> EthereumTokenApprovalTransaction:
        '''
        Create unsigned token approve transaction.

        Args:
            sender_address (str): wallet address of the sender
            value (str, Decimal): amount to swap
            token_address: address of the ERC20 token contract to swap

        Returns:
            EthereumTokenApprovalTransaction: unsigned token approve transaction

        Raises:
            ValueError: if you use an incorrect token address

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> network.approve_token('0x999F348959E611F1E9eab2927c21E88E48e6Ef45', '0.05', '0x53E546387A0d054e7FF127923254c0a679DA6DBf')  # noqa: E501
            <clove.network.ethereum.transaction.EthereumTokenApprovalTransaction at 0x7f286d14bc50>
        '''
        if not isinstance(value, Decimal):
            value = Decimal(str(value))

        token = None
        if token_address:
            token = self.get_token_by_address(token_address)
            if not token:
                logger.warning('Unknown ethereum token')
                raise ValueError('Unknown token')

        transaction = EthereumTokenApprovalTransaction(
            self,
            sender_address,
            value,
            token,
        )

        return transaction
Exemplo n.º 27
0
    def get_token_by_symbol(cls, symbol: str) -> Optional[EthToken]:
        '''
        Get a known token (from clove) by its symbol.

        Args:
            symbol (str): token symbol

        Returns:
            EthToken, None: Ethereum Token namedtuple or None if there is no matching token

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> network.get_token_by_symbol('PGT')
            <clove.network.ethereum.token.EthToken at 0x7f7b3fdffe48>
        '''
        token = cls.get_token_by_attribute('symbol', symbol)
        if not token:
            logger.warning(f'No token found for symbol {symbol}')
            return
        return EthToken.from_namedtuple(token)
Exemplo n.º 28
0
    def get_token_by_address(self, address: str) -> Optional[EthToken]:
        '''
        Get token by its address.

        Args:
            address (str): token address

        Returns:
            EthToken, None: Ethereum Token namedtuple

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> network.get_token_by_address('0x2c76B98079Bb5520FF4BDBC1bf5012AC3E87ddF6')
            <clove.network.ethereum.token.EthToken at 0x7f7b3fed1eb8>
        '''
        token = self.get_token_by_attribute(
            'address', address) or self.get_token_from_token_contract(address)
        if not token:
            logger.warning(f'No token found for address {address}')
            return
        return EthToken.from_namedtuple(token)
Exemplo n.º 29
0
    def extract_secret_from_redeem_transaction(cls, contract_address: str) -> Optional[str]:
        api_key = os.environ.get('CRYPTOID_API_KEY')
        if not api_key:
            raise ValueError('API key for cryptoid is required.')

        data = clove_req_json(f'{cls.cryptoid_url()}/api.dws?q=multiaddr&active={contract_address}&key={api_key}')
        if not data:
            logger.debug('Unexpected response from cryptoid')
            raise ValueError('Unexpected response from cryptoid')

        transactions = data['txs']
        if len(transactions) == 1:
            logger.debug('Contract was not redeemed yet.')
            return

        redeem_tx_hash = transactions[0]['hash']
        logger.warning('Using undocumented endpoint used by chainz.cryptoid.info site.')
        data = clove_req_json(f'{cls.api_url}/explorer/tx.raw.dws?coin={cls.symbols[0].lower()}&id={redeem_tx_hash}')
        if not data:
            logger.debug('Unexpected response from cryptoid')
            raise ValueError('Unexpected response from cryptoid')

        return cls.extract_secret(scriptsig=data['vin'][0]['scriptSig']['hex'])
Exemplo n.º 30
0
    def get_token_from_token_contract(self,
                                      token_address: str) -> Optional[Token]:
        '''
        Getting information from token contract (remote)

        Args:
            token_address (str): address of the token contract

        Returns:
            Token, None: Ethereum Token namedtuple or None if there is something goes wrong

        Raises:
            RuntimeError: if name or symbol of the token is not defined.

        Example:
            >>> from clove.network import EthereumTestnet
            >>> network = EthereumTestnet()
            >>> network.get_token_from_token_contract('0x2c76B98079Bb5520FF4BDBC1bf5012AC3E87ddF6')
            Token(name='PrettyGoodToken', symbol='PGT', address='0x2c76B98079Bb5520FF4BDBC1bf5012AC3E87ddF6', decimals=18)  # noqa: E501
        '''
        token_address = self.unify_address(token_address)
        token_contract = self.web3.eth.contract(address=token_address,
                                                abi=ERC20_BASIC_ABI)
        concise = ConciseContract(token_contract)
        try:
            name = concise.name()
            symbol = concise.symbol()
            decimals = concise.decimals()
            if name == '' or symbol == '':
                raise RuntimeError(
                    'Unable to extract token details from token contract')
            logger.debug(f'Token get from contract with success')
        except (OverflowError, BadFunctionCallOutput):
            logger.warning(
                f'Unable to take token from address: {token_address}')
            return
        return Token(name, symbol, token_address, decimals)