Repository: ethereum/web3.py

Issue:
Docs lack examples for "any-of" and "all-of" topic filters
* `web3.py` version: 3.8.1 (web3[gevent])
* node: `geth 1.6.5-stable`, local (via IPC)
* Python: 3.5
* OS: Ubuntu 16.04.2 LTS

### What seems wrong?

Retrieving past log events while specifying `'topics'` gives an empty list, if value of `'topics'` is a list with more than one element.

I was under the impression that the filter will match **any** event that's in `'topics'`. This seems not to be the case - EDIT: perhaps it's **all**?..

### Details

Using these variables:

``` python
> # common vars
> import cancelot
> cancelot.enslaunchblock
3648565
> cancelot.registrar
'0x6090a6e47849629b7245dfa1ca21d94cd15878ef'
> list(cancelot.handlers.keys())
['0x7b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7',
 '0xb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc29']
```

Not specifying `'topics'` does give a list of events (`dict`s), as expected:

``` python
> # filter for past events, without topics
> filtall =  web3.eth.filter({
    'fromBlock': cancelot.enslaunchblock,
    'toBlock': cancelot.enslaunchblock + 1000,
    'address': cancelot.registrar
    })
> evs = filtall.get(only_changes = False)
> len(evs)
80
> evs[0]
{'address': '0x6090a6e47849629b7245dfa1ca21d94cd15878ef',
 'blockHash': '0x3a2c9e10de45a86d39f52eeaa2beef514d8b0a19ddf6bd231a6d97bca79988b8',
 'blockNumber': 3648565,
 'data': '0x000000000000000000000000000000000000000000000000002386f26fc10000',
 'logIndex': 4,
 'removed': False,
 'topics': ['0xb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc29',
  '0x1f0e90602b7ed7fe12cd293824cccad3966e676d067df11da128793f5ad97ac3',
  '0x0000000000000000000000000904dac3347ea47d208f3fd67402d039a3b99859'],
 'transactionHash': '0x6e032eed213f1a641af8cdb7fd3770984010a38c04f6c06e393b03dfca30a80d',
 'transactionIndex': 40}
```

The above shows that the topic `0xb556..`, which is part of `list(cancelot.handlers.keys())`, is present as the first event log entry.

Giving a list of topics, though, doesn't work:

``` python
> # same filter, with topics
> filttop = web3.eth.filter({
    'fromBlock': cancelot.enslaunchblock,
    'toBlock': cancelot.enslaunchblock + 1000,
    'address': cancelot.registrar,
    'topics': list(cancelot.handlers.keys())
    })
> filttop.get(only_changes = False)
[]
```

### Providing topics manually

I know that the `0xb556..` topic is present for sure (it's the ENS `NewBid` event):

``` python
> # topic known to be present
> filtnew = web3.eth.filter({
    'fromBlock': cancelot.enslaunchblock,
    'toBlock': cancelot.enslaunchblock + 1000,
    'address': cancelot.registrar,
    'topics': ['0xb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc29']
    })
> len(filtnew.get(False))
31
```

And `0x7b6c..` is for sure not present (ENS `BidRevealed` event):

``` python
> # topic known to be not present
> filtrev = web3.eth.filter({
    'fromBlock': cancelot.enslaunchblock,
    'toBlock': cancelot.enslaunchblock + 1000,
    'address': cancelot.registrar,
    'topics': ['0x7b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7']
    })
> len(filtrev.get(False))
0
```

Specifying both results in empty list behaviour:

``` python
> # both topics
> filtboth = web3.eth.filter({'fromBlock': cancelot.enslaunchblock,
    'toBlock': cancelot.enslaunchblock + 1000,
    'address': cancelot.registrar,
    'topics': ['0x7b6c4b278d165a6b33958f8ea5dfb00c8c9d4d0acf1985bef5d10786898bc3e7', '0xb556ff269c1b6714f432c36431e2041d28436a73b6c3f19c021827bbdc6bfc29']
    })
> len(filtboth.get(False))
0
```

### Cute Animal Picture

Puffer fish can't believe it (via [some random article](http://www.dailymail.co.uk/sciencetech/article-2354496/Scientist-believe-humans-grow-beaks-instead-teeth-like-pufferfish.html)):

![Puffer fish](http://i.dailymail.co.uk/i/pix/2013/07/03/article-2354496-1AA2EC63000005DC-188_306x423.jpg)

Don't encourage using ConciseContract or ImplicitContract as ContractFactoryClass
### What was wrong?

Documentation encourages using `ConciseContract` and `ImplicitContract` as a `ContractFactoryClass`. They are not proper subclasses of `Contract`, which causes confusion when the API changes (like `deploy()` is no longer available)

### How can it be fixed?

1. Remove references to using these classes as a factory in the documentation
2. Show preferred implementation instead
3. Show using methods on the core contract before wrapping up in these convenience classes
2. Add a deprecation warning when using `ConciseContract` or `ImplicitContract` as a factory.

Preferred implementation of `ConciseContract`, with example core contract methods:
```py
TokenContract = w3.eth.contract(abi=...)

deploy_hash = TokenContract.deploy() 
# wait for deployment transaction to be mined
while True:
    receipt = w3.eth.getTransactionReceipt(deploy_hash)
    if receipt:
        break
    time.sleep(1)

token_contract = TokenContract(receipt['contractAddress'])
token = ConciseContract(token_contract)

 # return my balance
token.balanceOf(MY_ADDR)

 # returns my balance with the core contract (exactly equivalent to previous line)
token_contract.functions.balanceOf(MY_ADDR).call()
```


Discussion:
Perhaps I misunderstood the functionality, and the filter matches messages with *all* of the specified topics, not *any*.

This is also probably node-dependent.

If so, OK to close.
Re-read [the docs](https://web3py.readthedocs.io/en/latest/contracts.html#web3.contract.Contract.on):

> `topics`: list of 32 byte `strings` or `null` - (optional) Array of topics that should be used for filtering. Topics are order-dependent. This parameter can also be a list of topic lists in which case filtering will match any of the provided topic arrays.

So, perhaps what I needed was

``` python
[['<topic1>'], ['<topic2>'], ['<topic3>']]
```

instead of

``` python
['<topic1>', '<topic2>', '<topic3>']
```

Even if this is dependent on the node backend, it may be useful to clarify that dense passage in the docs with an example.
> Even if this is dependent on the node backend, it may be useful to clarify that dense passage in the docs with an example.

I think this would be a good idea.  Docs could use a lot more examples surrounding filtering.


Resolving patch:
diff --git a/web3/utils/events.py b/web3/utils/events.py
--- a/web3/utils/events.py
+++ b/web3/utils/events.py
@@ -112,13 +112,13 @@ def construct_event_data_set(event_abi, arguments=None):
         for arg, arg_options in zipped_abi_and_args
     ]
 
-    topics = [
+    data = [
         list(permutation)
         if any(value is not None for value in permutation)
         else []
         for permutation in itertools.product(*encoded_args)
     ]
-    return topics
+    return data
 
 
 def is_dynamic_sized_type(_type):


Test patch: