Beispiel #1
0
def select_filter_method(
    value: Union[str, FilterParams, HexStr],
    if_new_block_filter: RPCEndpoint,
    if_new_pending_transaction_filter: RPCEndpoint,
    if_new_filter: RPCEndpoint,
) -> RPCEndpoint:

    if is_string(value):
        if value == "latest":
            return if_new_block_filter
        elif value == "pending":
            return if_new_pending_transaction_filter
        elif is_hex(value):
            raise _UseExistingFilter(value)
        else:
            raise ValidationError(
                "Filter argument needs to be either 'latest',"
                " 'pending', or a hex-encoded filter_id. Filter argument"
                f" is: {value}")
    elif isinstance(value, dict):
        return if_new_filter
    else:
        raise ValidationError(
            "Filter argument needs to be either the string "
            "'pending' or 'latest', a filter_id, "
            f"or a filter params dictionary. Filter argument is: {value}")
Beispiel #2
0
def handle_offchain_lookup(
    offchain_lookup_payload: Dict[str, Any],
    transaction: TxParams,
) -> bytes:
    formatted_sender = to_hex_if_bytes(
        offchain_lookup_payload['sender']).lower()
    formatted_data = to_hex_if_bytes(
        offchain_lookup_payload['callData']).lower()

    if formatted_sender != to_hex_if_bytes(transaction['to']).lower():
        raise ValidationError(
            'Cannot handle OffchainLookup raised inside nested call. Returned `sender` value does '
            'not equal `to` address in transaction.')

    for url in offchain_lookup_payload['urls']:
        formatted_url = URI(
            str(url).replace('{sender}', str(formatted_sender)).replace(
                '{data}', str(formatted_data)))

        try:
            if '{data}' in url and '{sender}' in url:
                response = get_response_from_get_request(formatted_url)
            elif '{sender}' in url:
                response = get_response_from_post_request(formatted_url,
                                                          data={
                                                              "data":
                                                              formatted_data,
                                                              "sender":
                                                              formatted_sender,
                                                          })
            else:
                raise ValidationError('url not formatted properly.')
        except Exception:
            continue  # try next url if timeout or issues making the request

        if 400 <= response.status_code <= 499:  # if request returns 400 error, raise exception
            response.raise_for_status()
        if not 200 <= response.status_code <= 299:  # if not 400 error, try next url
            continue

        result = response.json()

        if 'data' not in result.keys():
            raise ValidationError(
                "Improperly formatted response for offchain lookup HTTP request - missing 'data' "
                "field.")

        encoded_data_with_function_selector = b''.join([
            # 4-byte callback function selector
            to_bytes_if_hex(offchain_lookup_payload['callbackFunction']),

            # encode the `data` from the result and the `extraData` as bytes
            encode_abi(['bytes', 'bytes'], [
                to_bytes_if_hex(result['data']),
                to_bytes_if_hex(offchain_lookup_payload['extraData']),
            ])
        ])

        return encoded_data_with_function_selector
    raise MultipleFailedRequests("Offchain lookup failed for supplied urls.")
Beispiel #3
0
def _validate_chain_id(web3_chain_id: int, chain_id: int) -> int:
    if to_integer_if_hex(chain_id) == web3_chain_id:
        return chain_id
    else:
        raise ValidationError(
            f"The transaction declared chain ID {chain_id!r}, "
            f"but the connected node is on {web3_chain_id!r}")
Beispiel #4
0
def attach_modules(
    parent_module: Union["Web3", "Module"],
    module_definitions: Dict[str, Sequence[Any]],
    w3: Optional[Union["Web3", "Module"]] = None
) -> None:
    for module_name, module_info in module_definitions.items():
        module_class = module_info[0]

        if hasattr(parent_module, module_name):
            raise AttributeError(
                f"Cannot set {parent_module} module named '{module_name}'.  The web3 object "
                "already has an attribute with that name"
            )

        if w3 is None:
            setattr(parent_module, module_name, module_class(parent_module))
            w3 = parent_module
        else:
            setattr(parent_module, module_name, module_class(w3))

        if len(module_info) == 2:
            submodule_definitions = module_info[1]
            module = getattr(parent_module, module_name)
            attach_modules(module, submodule_definitions, w3)
        elif len(module_info) != 1:
            raise ValidationError("Module definitions can only have 1 or 2 elements.")
def find_matching_fn_abi(abi,
                         abi_codec,
                         fn_identifier=None,
                         args=None,
                         kwargs=None):
    args = args or tuple()
    kwargs = kwargs or dict()
    num_arguments = len(args) + len(kwargs)

    if fn_identifier is FallbackFn:
        return get_fallback_func_abi(abi)

    if not is_text(fn_identifier):
        raise TypeError("Unsupported function identifier")

    name_filter = functools.partial(filter_by_name, fn_identifier)
    arg_count_filter = functools.partial(filter_by_argument_count,
                                         num_arguments)
    encoding_filter = functools.partial(filter_by_encodability, abi_codec,
                                        args, kwargs)

    function_candidates = pipe(abi, name_filter, arg_count_filter,
                               encoding_filter)

    if len(function_candidates) == 1:
        return function_candidates[0]
    else:
        matching_identifiers = name_filter(abi)
        matching_function_signatures = [
            abi_to_signature(func) for func in matching_identifiers
        ]

        arg_count_matches = len(arg_count_filter(matching_identifiers))
        encoding_matches = len(encoding_filter(matching_identifiers))

        if arg_count_matches == 0:
            diagnosis = "\nFunction invocation failed due to improper number of arguments."
        elif encoding_matches == 0:
            diagnosis = "\nFunction invocation failed due to no matching argument types."
        elif encoding_matches > 1:
            diagnosis = (
                "\nAmbiguous argument encoding. "
                "Provided arguments can be encoded to multiple functions matching this call."
            )

        message = (
            "\nCould not identify the intended function with name `{name}`, "
            "positional argument(s) of type `{arg_types}` and "
            "keyword argument(s) of type `{kwarg_types}`."
            "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}"
            "{diagnosis}").format(
                name=fn_identifier,
                arg_types=tuple(map(type, args)),
                kwarg_types=valmap(type, kwargs),
                num_candidates=len(matching_identifiers),
                candidates=matching_function_signatures,
                diagnosis=diagnosis,
            )

        raise ValidationError(message)
Beispiel #6
0
 def __init__(
     self,
     endpoint_uri: Optional[Union[URI, str]] = None,
     websocket_kwargs: Any = None,
     websocket_timeout: int = DEFAULT_WEBSOCKET_TIMEOUT,
 ) -> None:
     self.endpoint_uri = URI(endpoint_uri)
     self.websocket_timeout = websocket_timeout
     if self.endpoint_uri is None:
         self.endpoint_uri = get_default_endpoint()
     if WebsocketProvider._loop is None:
         WebsocketProvider._loop = _get_threaded_loop()
     if websocket_kwargs is None:
         websocket_kwargs = {}
     else:
         found_restricted_keys = set(websocket_kwargs.keys()).intersection(
             RESTRICTED_WEBSOCKET_KWARGS)
         if found_restricted_keys:
             raise ValidationError(
                 '{0} are not allowed in websocket_kwargs, '
                 'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS,
                                     found_restricted_keys))
     self.conn = PersistentWebSocket(self.endpoint_uri,
                                     WebsocketProvider._loop,
                                     websocket_kwargs)
     super().__init__()
Beispiel #7
0
    def buy_at_fixed_rate(
        self,
        amount: float,
        wallet: Wallet,
        max_OCEAN_amount: float,
        exchange_id: str = "",
        data_token: str = "",
        exchange_owner: str = "",
    ) -> bool:

        exchange, exchange_id = self.get_exchange_id_fallback_dt_and_owner(
            exchange_id, exchange_owner, data_token)

        amount_base = to_base_18(amount)
        max_OCEAN_amount_base = to_base_18(max_OCEAN_amount)

        # Figure out the amount of ocean tokens to approve before triggering the exchange function to do the swap
        ocean_amount_base = exchange.get_base_token_quote(
            exchange_id, amount_base)
        if ocean_amount_base > max_OCEAN_amount_base:
            raise ValidationError(
                f"Buying {amount} datatokens requires {from_base_18(ocean_amount_base)} OCEAN "
                f"tokens which exceeds the max_OCEAN_amount {max_OCEAN_amount}."
            )
        ocean_token = DataToken(self.ocean_address)
        ocean_token.get_tx_receipt(
            ocean_token.approve(self._exchange_address, ocean_amount_base,
                                wallet))
        tx_id = exchange.buy_data_token(exchange_id,
                                        data_token_amount=amount_base,
                                        from_wallet=wallet)
        return bool(exchange.get_tx_receipt(tx_id).status)
Beispiel #8
0
def find_matching_fn_abi(
    abi: ABI,
    abi_codec: ABICodec,
    fn_identifier: Optional[Union[str, Type[FallbackFn],
                                  Type[ReceiveFn]]] = None,
    args: Optional[Sequence[Any]] = None,
    kwargs: Optional[Any] = None,
) -> ABIFunction:
    args = args or tuple()
    kwargs = kwargs or dict()
    num_arguments = len(args) + len(kwargs)

    if fn_identifier is FallbackFn:
        return get_fallback_func_abi(abi)

    if fn_identifier is ReceiveFn:
        return get_receive_func_abi(abi)

    if not is_text(fn_identifier):
        raise TypeError("Unsupported function identifier")

    name_filter = functools.partial(filter_by_name, fn_identifier)
    arg_count_filter = functools.partial(filter_by_argument_count,
                                         num_arguments)
    encoding_filter = functools.partial(filter_by_encodability, abi_codec,
                                        args, kwargs)

    function_candidates = pipe(abi, name_filter, arg_count_filter,
                               encoding_filter)

    if len(function_candidates) == 1:
        return function_candidates[0]
    else:
        matching_identifiers = name_filter(abi)
        matching_function_signatures = [
            abi_to_signature(func) for func in matching_identifiers
        ]

        arg_count_matches = len(arg_count_filter(matching_identifiers))
        encoding_matches = len(encoding_filter(matching_identifiers))

        if arg_count_matches == 0:
            diagnosis = "\nFunction invocation failed due to improper number of arguments."
        elif encoding_matches == 0:
            diagnosis = "\nFunction invocation failed due to no matching argument types."
        elif encoding_matches > 1:
            diagnosis = (
                "\nAmbiguous argument encoding. "
                "Provided arguments can be encoded to multiple functions matching this call."
            )
        message = (
            f"\nCould not identify the intended function with name `{fn_identifier}`, positional "
            f"argument(s) of type `{tuple(map(type, args))}` and keyword argument(s) of type "
            f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} function(s) with "
            f"the name `{fn_identifier}`: {matching_function_signatures}{diagnosis}"
        )

        raise ValidationError(message)
Beispiel #9
0
def _validate_chain_id(web3_chain_id: int, chain_id: int) -> int:
    if to_integer_if_hex(chain_id) == web3_chain_id:
        return chain_id
    else:
        raise ValidationError("The transaction declared chain ID %r, "
                              "but the connected node is on %r" % (
                                  chain_id,
                                  web3_chain_id,
                              ))
Beispiel #10
0
def validate_chain_id(web3, chain_id):
    if chain_id == web3.net.chainId:
        return chain_id
    else:
        raise ValidationError("The transaction declared chain ID %r, "
                              "but the connected node is on %r" % (
                                  chain_id,
                                  "UNKNOWN",
                              ))
Beispiel #11
0
def validate_chain_id(web3, chain_id):
    if chain_id == web3.version.network:
        return None
    else:
        raise ValidationError("The transaction declared chain ID %r, "
                              "but the connected node is on %r" % (
                                  chain_id,
                                  web3.version.network,
                              ))
Beispiel #12
0
def validate_chain_id(web3, chain_id):
    if int(chain_id) == web3.eth.chainId:
        return chain_id
    else:
        raise ValidationError("The transaction declared chain ID %r, "
                              "but the connected node is on %r" % (
                                  chain_id,
                                  web3.eth.chainId,
                              ))
Beispiel #13
0
def _get_avg_block_time(w3, sample_size):
    latest = w3.eth.getBlock('latest')

    constrained_sample_size = min(sample_size, latest['number'])
    if constrained_sample_size == 0:
        raise ValidationError('Constrained sample size is 0')

    oldest = w3.eth.getBlock(latest['number'] - constrained_sample_size)
    return (latest['timestamp'] - oldest['timestamp']) / constrained_sample_size
Beispiel #14
0
def validate_payable(transaction, abi):
    """Raise ValidationError if non-zero ether
    is sent to a non payable function.
    """
    if 'value' in transaction:
        if transaction['value'] != 0:
            if "payable" in abi and not abi["payable"]:
                raise ValidationError(
                    "Sending non-zero ether to a contract function "
                    "with payable=False. Please ensure that "
                    "transaction's value is 0.")
Beispiel #15
0
def attach_modules(parent_module, module_definitions):
    for module_name, module_info in module_definitions.items():
        module_class = module_info[0]
        module_class.attach(parent_module, module_name)

        if len(module_info) == 2:
            submodule_definitions = module_info[1]
            module = getattr(parent_module, module_name)
            attach_modules(module, submodule_definitions)
        elif len(module_info) != 1:
            raise ValidationError(
                "Module definitions can only have 1 or 2 elements.")
Beispiel #16
0
def check_extradata_length(val):
    if not isinstance(val, (str, int, bytes)):
        return val
    result = HexBytes(val)
    if len(result) > MAX_EXTRADATA_LENGTH:
        raise ValidationError(
            "The field extraData is %d bytes, but should be %d. "
            "It is quite likely that you are connected to a POA chain. "
            "Refer "
            "http://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority "
            "for more details. The full extraData is: %r" %
            (len(result), MAX_EXTRADATA_LENGTH, result))
    return val
Beispiel #17
0
def _get_weighted_avg_block_time(w3: Web3, sample_size: int) -> float:
    latest_block_number = w3.eth.getBlock('latest')['number']
    constrained_sample_size = min(sample_size, latest_block_number)
    if constrained_sample_size == 0:
        raise ValidationError('Constrained sample size is 0')

    oldest_block = w3.eth.getBlock(BlockNumber(latest_block_number - constrained_sample_size))
    oldest_block_number = oldest_block['number']
    prev_timestamp = oldest_block['timestamp']
    weighted_sum = 0.0
    sum_of_weights = 0.0
    for i in range(oldest_block_number + 1, latest_block_number + 1):
        curr_timestamp = w3.eth.getBlock(BlockNumber(i))['timestamp']
        time = curr_timestamp - prev_timestamp
        weight = (i - oldest_block_number) / constrained_sample_size
        weighted_sum += (time * weight)
        sum_of_weights += weight
        prev_timestamp = curr_timestamp
    return weighted_sum / sum_of_weights
Beispiel #18
0
 def __init__(self, endpoint_uri=None, websocket_kwargs=None):
     self.endpoint_uri = endpoint_uri
     if self.endpoint_uri is None:
         self.endpoint_uri = get_default_endpoint()
     if WebsocketProvider._loop is None:
         WebsocketProvider._loop = _get_threaded_loop()
     if websocket_kwargs is None:
         websocket_kwargs = {}
     else:
         found_restricted_keys = set(websocket_kwargs.keys()).intersection(
             RESTRICTED_WEBSOCKET_KWARGS
         )
         if found_restricted_keys:
             raise ValidationError(
                 '{0} are not allowed in websocket_kwargs, '
                 'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys)
             )
     self.conn = PersistentWebSocket(
         self.endpoint_uri, WebsocketProvider._loop, websocket_kwargs
     )
     super().__init__()
    def buy_at_fixed_rate(
        self,
        amount: int,
        wallet: Wallet,
        max_OCEAN_amount: int,
        exchange_id: Optional[Union[bytes, str]] = "",
        data_token: Optional[str] = "",
        exchange_owner: Optional[str] = "",
    ) -> bool:

        exchange, exchange_id = self.get_exchange_id_fallback_dt_and_owner(
            exchange_id, exchange_owner, data_token)

        # Figure out the amount of ocean tokens to approve before triggering the exchange function to do the swap
        ocean_amount = exchange.get_base_token_quote(exchange_id, amount)
        ocean_token = DataToken(self._web3, self.ocean_address)
        ocean_ticker = ocean_token.symbol()
        if ocean_amount > max_OCEAN_amount:
            raise ValidationError(
                f"Buying {pretty_ether_and_wei(amount, 'DataTokens')} requires {pretty_ether_and_wei(ocean_amount, ocean_ticker)} "
                f"tokens which exceeds the max_OCEAN_amount {pretty_ether_and_wei(max_OCEAN_amount, ocean_ticker)}."
            )
        if ocean_token.balanceOf(wallet.address) < ocean_amount:
            raise InsufficientBalance(
                f"Insufficient funds for buying {pretty_ether_and_wei(amount, 'DataTokens')}!"
            )
        if ocean_token.allowance(wallet.address,
                                 self._exchange_address) < ocean_amount:
            tx_id = ocean_token.approve(self._exchange_address, ocean_amount,
                                        wallet)
            tx_receipt = ocean_token.get_tx_receipt(self._web3, tx_id)
            if not tx_receipt or tx_receipt.status != 1:
                raise VerifyTxFailed(
                    f"Approve OCEAN tokens failed, exchange address was {self._exchange_address} and tx id was {tx_id}!"
                )
        tx_id = exchange.buy_data_token(exchange_id,
                                        data_token_amount=amount,
                                        from_wallet=wallet)
        return bool(exchange.get_tx_receipt(self._web3, tx_id).status)
Beispiel #20
0
    def getLogs(
        self,
        event,
        web3,
        argument_filters: Optional[Dict[str, Any]] = None,
        fromBlock: Optional[BlockIdentifier] = None,
        toBlock: Optional[BlockIdentifier] = None,
        blockHash: Optional[HexBytes] = None,
    ):
        """Get events for this contract instance using eth_getLogs API.

        This is a stateless method, as opposed to createFilter.
        It can be safely called against nodes which do not provide
        eth_newFilter API, like Infura nodes.
        If there are many events,
        like ``Transfer`` events for a popular token,
        the Ethereum node might be overloaded and timeout
        on the underlying JSON-RPC call.
        Example - how to get all ERC-20 token transactions
        for the latest 10 blocks:
        .. code-block:: python
            from = max(mycontract.web3.eth.blockNumber - 10, 1)
            to = mycontract.web3.eth.blockNumber
            events = mycontract.events.Transfer.getLogs(fromBlock=from, toBlock=to)
            for e in events:
                print(e["args"]["from"],
                    e["args"]["to"],
                    e["args"]["value"])
        The returned processed log values will look like:
        .. code-block:: python
            (
                AttributeDict({
                 'args': AttributeDict({}),
                 'event': 'LogNoArguments',
                 'logIndex': 0,
                 'transactionIndex': 0,
                 'transactionHash': HexBytes('...'),
                 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b',
                 'blockHash': HexBytes('...'),
                 'blockNumber': 3
                }),
                AttributeDict(...),
                ...
            )
        See also: :func:`web3.middleware.filter.local_filter_middleware`.
        :param argument_filters:
        :param fromBlock: block number or "latest", defaults to "latest"
        :param toBlock: block number or "latest". Defaults to "latest"
        :param blockHash: block hash. blockHash cannot be set at the
          same time as fromBlock or toBlock
        :yield: Tuple of :class:`AttributeDict` instances
        """
        if not self.address:
            raise TypeError(
                "This method can be only called on "
                "an instated contract with an address"
            )

        abi = event._get_event_abi()

        if argument_filters is None:
            argument_filters = dict()

        _filters = dict(**argument_filters)

        blkhash_set = blockHash is not None
        blknum_set = fromBlock is not None or toBlock is not None
        if blkhash_set and blknum_set:
            raise ValidationError(
                "blockHash cannot be set at the same" " time as fromBlock or toBlock"
            )

        # Construct JSON-RPC raw filter presentation based on human readable Python descriptions
        # Namely, convert event names to their keccak signatures
        _, event_filter_params = construct_event_filter_params(
            abi,
            contract_address=self.address,
            argument_filters=_filters,
            fromBlock=fromBlock,
            toBlock=toBlock,
        )

        if blockHash is not None:
            event_filter_params["blockHash"] = blockHash

        # Call JSON-RPC API
        logs = web3.eth.getLogs(event_filter_params)

        # Convert raw binary data to Python proxy objects as described by ABI
        return tuple(get_event_data(abi, entry) for entry in logs)