mirror of
https://github.com/FlipsideCrypto/web3.py.git
synced 2026-02-06 10:56:47 +00:00
Event, Block, and Transaction Filtering
This commit is contained in:
parent
12334f6f93
commit
18eedaaaf6
@ -282,3 +282,104 @@ def StringContract(web3_tester, string_contract_abi_def):
|
||||
code_runtime=string_contract_abi_def["code_runtime"],
|
||||
source=string_contract_abi_def["source"],
|
||||
)
|
||||
|
||||
|
||||
CONTRACT_EMITTER_SOURCE = textwrap.dedent(("""
|
||||
contract Emitter {
|
||||
event LogAnonymous() anonymous;
|
||||
event LogNoArguments();
|
||||
event LogSingleArg(uint arg0);
|
||||
event LogDoubleArg(uint arg0, uint arg1);
|
||||
event LogTripleArg(uint arg0, uint arg1, uint arg2);
|
||||
event LogQuadrupleArg(uint arg0, uint arg1, uint arg2, uint arg3);
|
||||
|
||||
// Indexed
|
||||
event LogSingleWithIndex(uint indexed arg0);
|
||||
event LogDoubleWithIndex(uint arg0, uint indexed arg1);
|
||||
event LogTripleWithIndex(uint arg0, uint indexed arg1, uint indexed arg2);
|
||||
event LogQuadrupleWithIndex(uint arg0, uint arg1, uint indexed arg2, uint indexed arg3);
|
||||
|
||||
enum WhichEvent {
|
||||
LogAnonymous,
|
||||
LogNoArguments,
|
||||
LogSingleArg,
|
||||
LogDoubleArg,
|
||||
LogTripleArg,
|
||||
LogQuadrupleArg,
|
||||
LogSingleWithIndex,
|
||||
LogDoubleWithIndex,
|
||||
LogTripleWithIndex,
|
||||
LogQuadrupleWithIndex
|
||||
}
|
||||
|
||||
function logNoArgs(WhichEvent which) public {
|
||||
if (which == WhichEvent.LogNoArguments) LogNoArguments();
|
||||
else if (which == WhichEvent.LogAnonymous) LogAnonymous();
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logSingle(WhichEvent which, uint arg0) public {
|
||||
if (which == WhichEvent.LogSingleArg) LogSingleArg(arg0);
|
||||
else if (which == WhichEvent.LogSingleWithIndex) LogSingleWithIndex(arg0);
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logDouble(WhichEvent which, uint arg0, uint arg1) public {
|
||||
if (which == WhichEvent.LogDoubleArg) LogDoubleArg(arg0, arg1);
|
||||
else if (which == WhichEvent.LogDoubleWithIndex) LogDoubleWithIndex(arg0, arg1);
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logTriple(WhichEvent which, uint arg0, uint arg1, uint arg2) public {
|
||||
if (which == WhichEvent.LogTripleArg) LogTripleArg(arg0, arg1, arg2);
|
||||
else if (which == WhichEvent.LogTripleWithIndex) LogTripleWithIndex(arg0, arg1, arg2);
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logQuadruple(WhichEvent which, uint arg0, uint arg1, uint arg2, uint arg3) public {
|
||||
if (which == WhichEvent.LogQuadrupleArg) LogQuadrupleArg(arg0, arg1, arg2, arg3);
|
||||
else if (which == WhichEvent.LogQuadrupleWithIndex) LogQuadrupleWithIndex(arg0, arg1, arg2, arg3);
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
"""))
|
||||
|
||||
CONTRACT_EMITTER_CODE = "0x60606040526102c0806100126000396000f3606060405260e060020a600035046317c0c180811461004757806320f0256e1461008057806390b41d8b146100da5780639c37705314610125578063aa6fd82214610177575b005b61004560043560018114156101b9577f1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c60006060a15b50565b61004560043560243560443560643560843560058514156101d1576060848152608084815260a084905260c08390527ff039d147f23fe975a4254bdf6b1502b8c79132ae1833986b7ccef2638e73fdf991a15b5050505050565b610045600435602435604435600383141561021357606082815260808290527fdf0cb1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc590604090a15b505050565b610045600435602435604435606435600484141561024e576060838152608083905260a08290527f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f550629080a15b50505050565b610045600435602435600282141561028b5760608181527f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d490602090a15b5050565b60008114156101cc5760006060a061007d565b610002565b60098514156101cc5760608481526080849052819083907fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad955b590604090a36100d3565b60078314156101cc57606082815281907f057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca90602090a2610120565b60088414156101cc576060838152819083907ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd42ee6ec90602090a3610171565b60068214156101cc57807ff70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560006060a26101b556"
|
||||
|
||||
CONTRACT_EMITTER_RUNTIME = "0x606060405260e060020a600035046317c0c180811461004757806320f0256e1461008057806390b41d8b146100da5780639c37705314610125578063aa6fd82214610177575b005b61004560043560018114156101b9577f1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c60006060a15b50565b61004560043560243560443560643560843560058514156101d1576060848152608084815260a084905260c08390527ff039d147f23fe975a4254bdf6b1502b8c79132ae1833986b7ccef2638e73fdf991a15b5050505050565b610045600435602435604435600383141561021357606082815260808290527fdf0cb1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc590604090a15b505050565b610045600435602435604435606435600484141561024e576060838152608083905260a08290527f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f550629080a15b50505050565b610045600435602435600282141561028b5760608181527f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d490602090a15b5050565b60008114156101cc5760006060a061007d565b610002565b60098514156101cc5760608481526080849052819083907fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad955b590604090a36100d3565b60078314156101cc57606082815281907f057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca90602090a2610120565b60088414156101cc576060838152819083907ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd42ee6ec90602090a3610171565b60068214156101cc57807ff70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560006060a26101b556"
|
||||
|
||||
CONTRACT_EMITTER_ABI = json.loads('[{"constant":false,"inputs":[{"name":"which","type":"uint8"}],"name":"logNoArgs","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"},{"name":"arg2","type":"uint256"},{"name":"arg3","type":"uint256"}],"name":"logQuadruple","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"}],"name":"logDouble","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"},{"name":"arg2","type":"uint256"}],"name":"logTriple","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"}],"name":"logSingle","outputs":[],"type":"function"},{"anonymous":true,"inputs":[],"name":"LogAnonymous","type":"event"},{"anonymous":false,"inputs":[],"name":"LogNoArguments","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"}],"name":"LogSingleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"}],"name":"LogDoubleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":false,"name":"arg2","type":"uint256"}],"name":"LogTripleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":false,"name":"arg2","type":"uint256"},{"indexed":false,"name":"arg3","type":"uint256"}],"name":"LogQuadrupleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"arg0","type":"uint256"}],"name":"LogSingleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":true,"name":"arg1","type":"uint256"}],"name":"LogDoubleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":true,"name":"arg1","type":"uint256"},{"indexed":true,"name":"arg2","type":"uint256"}],"name":"LogTripleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":true,"name":"arg2","type":"uint256"},{"indexed":true,"name":"arg3","type":"uint256"}],"name":"LogQuadrupleWithIndex","type":"event"}]')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_SOURCE():
|
||||
return CONTRACT_EMITTER_SOURCE
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_CODE():
|
||||
return CONTRACT_EMITTER_CODE
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_RUNTIME():
|
||||
return CONTRACT_EMITTER_RUNTIME
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_ABI():
|
||||
return CONTRACT_EMITTER_ABI
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EmitterContract(web3_tester,
|
||||
EMITTER_SOURCE,
|
||||
EMITTER_CODE,
|
||||
EMITTER_RUNTIME,
|
||||
EMITTER_ABI):
|
||||
return web3_tester.eth.contract(
|
||||
abi=EMITTER_ABI,
|
||||
code=EMITTER_CODE,
|
||||
code_runtime=EMITTER_RUNTIME,
|
||||
source=EMITTER_SOURCE,
|
||||
)
|
||||
|
||||
@ -42,7 +42,7 @@ def test_contract_estimateGas(web3, math_contract):
|
||||
if isinstance(web3.currentProvider, TestRPCProvider):
|
||||
pytest.skip("The testrpc server doesn't implement `eth_estimateGas`")
|
||||
|
||||
increment_abi = math_contract.find_matching_abi('increment', [])
|
||||
increment_abi = math_contract.find_matching_fn_abi('increment', [])
|
||||
call_data = function_abi_to_4byte_selector(increment_abi)
|
||||
gas_estimate = math_contract.estimateGas().increment()
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ MULTIPLE_FUNCTIONS = json.loads('[{"constant":false,"inputs":[],"name":"a","outp
|
||||
def test_finds_single_function_without_args(web3_tester):
|
||||
Contract = web3_tester.eth.contract(SINGLE_FN_NO_ARGS)
|
||||
|
||||
abi = Contract.find_matching_abi('a', [])
|
||||
abi = Contract.find_matching_fn_abi('a', [])
|
||||
assert abi['name'] == 'a'
|
||||
assert abi['inputs'] == []
|
||||
|
||||
@ -22,7 +22,7 @@ def test_finds_single_function_without_args(web3_tester):
|
||||
def test_finds_single_function_with_args(web3_tester):
|
||||
Contract = web3_tester.eth.contract(SINGLE_FN_ONE_ARG)
|
||||
|
||||
abi = Contract.find_matching_abi('a', [1234])
|
||||
abi = Contract.find_matching_fn_abi('a', [1234])
|
||||
assert abi['name'] == 'a'
|
||||
assert len(abi['inputs']) == 1
|
||||
assert abi['inputs'][0]['type'] == 'uint256'
|
||||
@ -32,7 +32,7 @@ def test_error_when_no_function_name_match(web3_tester):
|
||||
Contract = web3_tester.eth.contract(SINGLE_FN_NO_ARGS)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
Contract.find_matching_abi('no_function_name', [1234])
|
||||
Contract.find_matching_fn_abi('no_function_name', [1234])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -48,7 +48,7 @@ def test_error_when_no_function_name_match(web3_tester):
|
||||
def test_finds_function_with_matching_args(web3_tester, arguments, expected_types):
|
||||
Contract = web3_tester.eth.contract(MULTIPLE_FUNCTIONS)
|
||||
|
||||
abi = Contract.find_matching_abi('a', arguments)
|
||||
abi = Contract.find_matching_fn_abi('a', arguments)
|
||||
assert abi['name'] == 'a'
|
||||
assert len(abi['inputs']) == len(expected_types)
|
||||
assert set(get_abi_input_types(abi)) == set(expected_types)
|
||||
@ -58,4 +58,4 @@ def test_error_when_duplicate_match(web3_tester):
|
||||
Contract = web3_tester.eth.contract(MULTIPLE_FUNCTIONS)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
abi = Contract.find_matching_abi('a', [100])
|
||||
abi = Contract.find_matching_fn_abi('a', [100])
|
||||
|
||||
@ -42,7 +42,7 @@ def test_eth_estimateGas(web3, math_contract):
|
||||
if isinstance(web3.currentProvider, TestRPCProvider):
|
||||
pytest.skip("The testrpc server doesn't implement `eth_estimateGas`")
|
||||
|
||||
increment_abi = math_contract.find_matching_abi('increment', [])
|
||||
increment_abi = math_contract.find_matching_fn_abi('increment', [])
|
||||
call_data = function_abi_to_4byte_selector(increment_abi)
|
||||
gas_estimate = web3.eth.estimateGas({
|
||||
'to': math_contract.address,
|
||||
|
||||
175
tests/filtering/conftest.py
Normal file
175
tests/filtering/conftest.py
Normal file
@ -0,0 +1,175 @@
|
||||
import pytest
|
||||
import json
|
||||
import textwrap
|
||||
from sha3 import sha3_256
|
||||
|
||||
from web3.providers.rpc import TestRPCProvider
|
||||
|
||||
|
||||
assert sha3_256(b'').hexdigest() == 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def skip_testrpc_and_wait_for_mining_start(web3, wait_for_block):
|
||||
if isinstance(web3.currentProvider, TestRPCProvider):
|
||||
pytest.skip("No miner interface on eth-testrpc")
|
||||
|
||||
wait_for_block(web3)
|
||||
|
||||
|
||||
CONTRACT_EMITTER_SOURCE = textwrap.dedent(("""
|
||||
contract Emitter {
|
||||
event LogAnonymous() anonymous;
|
||||
event LogNoArguments();
|
||||
event LogSingleArg(uint arg0);
|
||||
event LogDoubleArg(uint arg0, uint arg1);
|
||||
event LogTripleArg(uint arg0, uint arg1, uint arg2);
|
||||
event LogQuadrupleArg(uint arg0, uint arg1, uint arg2, uint arg3);
|
||||
|
||||
// Indexed
|
||||
event LogSingleWithIndex(uint indexed arg0);
|
||||
event LogDoubleWithIndex(uint arg0, uint indexed arg1);
|
||||
event LogTripleWithIndex(uint arg0, uint indexed arg1, uint indexed arg2);
|
||||
event LogQuadrupleWithIndex(uint arg0, uint arg1, uint indexed arg2, uint indexed arg3);
|
||||
|
||||
enum WhichEvent {
|
||||
LogAnonymous,
|
||||
LogNoArguments,
|
||||
LogSingleArg,
|
||||
LogDoubleArg,
|
||||
LogTripleArg,
|
||||
LogQuadrupleArg,
|
||||
LogSingleWithIndex,
|
||||
LogDoubleWithIndex,
|
||||
LogTripleWithIndex,
|
||||
LogQuadrupleWithIndex
|
||||
}
|
||||
|
||||
function logNoArgs(WhichEvent which) public {
|
||||
if (which == WhichEvent.LogNoArguments) LogNoArguments();
|
||||
else if (which == WhichEvent.LogAnonymous) LogAnonymous();
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logSingle(WhichEvent which, uint arg0) public {
|
||||
if (which == WhichEvent.LogSingleArg) LogSingleArg(arg0);
|
||||
else if (which == WhichEvent.LogSingleWithIndex) LogSingleWithIndex(arg0);
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logDouble(WhichEvent which, uint arg0, uint arg1) public {
|
||||
if (which == WhichEvent.LogDoubleArg) LogDoubleArg(arg0, arg1);
|
||||
else if (which == WhichEvent.LogDoubleWithIndex) LogDoubleWithIndex(arg0, arg1);
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logTriple(WhichEvent which, uint arg0, uint arg1, uint arg2) public {
|
||||
if (which == WhichEvent.LogTripleArg) LogTripleArg(arg0, arg1, arg2);
|
||||
else if (which == WhichEvent.LogTripleWithIndex) LogTripleWithIndex(arg0, arg1, arg2);
|
||||
else throw;
|
||||
}
|
||||
|
||||
function logQuadruple(WhichEvent which, uint arg0, uint arg1, uint arg2, uint arg3) public {
|
||||
if (which == WhichEvent.LogQuadrupleArg) LogQuadrupleArg(arg0, arg1, arg2, arg3);
|
||||
else if (which == WhichEvent.LogQuadrupleWithIndex) LogQuadrupleWithIndex(arg0, arg1, arg2, arg3);
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
"""))
|
||||
|
||||
CONTRACT_EMITTER_CODE = "0x60606040526102c0806100126000396000f3606060405260e060020a600035046317c0c180811461004757806320f0256e1461008057806390b41d8b146100da5780639c37705314610125578063aa6fd82214610177575b005b61004560043560018114156101b9577f1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c60006060a15b50565b61004560043560243560443560643560843560058514156101d1576060848152608084815260a084905260c08390527ff039d147f23fe975a4254bdf6b1502b8c79132ae1833986b7ccef2638e73fdf991a15b5050505050565b610045600435602435604435600383141561021357606082815260808290527fdf0cb1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc590604090a15b505050565b610045600435602435604435606435600484141561024e576060838152608083905260a08290527f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f550629080a15b50505050565b610045600435602435600282141561028b5760608181527f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d490602090a15b5050565b60008114156101cc5760006060a061007d565b610002565b60098514156101cc5760608481526080849052819083907fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad955b590604090a36100d3565b60078314156101cc57606082815281907f057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca90602090a2610120565b60088414156101cc576060838152819083907ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd42ee6ec90602090a3610171565b60068214156101cc57807ff70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560006060a26101b556"
|
||||
|
||||
CONTRACT_EMITTER_RUNTIME = "0x606060405260e060020a600035046317c0c180811461004757806320f0256e1461008057806390b41d8b146100da5780639c37705314610125578063aa6fd82214610177575b005b61004560043560018114156101b9577f1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c60006060a15b50565b61004560043560243560443560643560843560058514156101d1576060848152608084815260a084905260c08390527ff039d147f23fe975a4254bdf6b1502b8c79132ae1833986b7ccef2638e73fdf991a15b5050505050565b610045600435602435604435600383141561021357606082815260808290527fdf0cb1dea99afceb3ea698d62e705b736f1345a7eee9eb07e63d1f8f556c1bc590604090a15b505050565b610045600435602435604435606435600484141561024e576060838152608083905260a08290527f4a25b279c7c585f25eda9788ac9420ebadae78ca6b206a0e6ab488fd81f550629080a15b50505050565b610045600435602435600282141561028b5760608181527f56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d490602090a15b5050565b60008114156101cc5760006060a061007d565b610002565b60098514156101cc5760608481526080849052819083907fa30ece802b64cd2b7e57dabf4010aabf5df26d1556977affb07b98a77ad955b590604090a36100d3565b60078314156101cc57606082815281907f057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca90602090a2610120565b60088414156101cc576060838152819083907ff16c999b533366ca5138d78e85da51611089cd05749f098d6c225d4cd42ee6ec90602090a3610171565b60068214156101cc57807ff70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb1560006060a26101b556"
|
||||
|
||||
CONTRACT_EMITTER_ABI = json.loads('[{"constant":false,"inputs":[{"name":"which","type":"uint8"}],"name":"logNoArgs","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"},{"name":"arg2","type":"uint256"},{"name":"arg3","type":"uint256"}],"name":"logQuadruple","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"}],"name":"logDouble","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"},{"name":"arg1","type":"uint256"},{"name":"arg2","type":"uint256"}],"name":"logTriple","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"which","type":"uint8"},{"name":"arg0","type":"uint256"}],"name":"logSingle","outputs":[],"type":"function"},{"anonymous":true,"inputs":[],"name":"LogAnonymous","type":"event"},{"anonymous":false,"inputs":[],"name":"LogNoArguments","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"}],"name":"LogSingleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"}],"name":"LogDoubleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":false,"name":"arg2","type":"uint256"}],"name":"LogTripleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":false,"name":"arg2","type":"uint256"},{"indexed":false,"name":"arg3","type":"uint256"}],"name":"LogQuadrupleArg","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"arg0","type":"uint256"}],"name":"LogSingleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":true,"name":"arg1","type":"uint256"}],"name":"LogDoubleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":true,"name":"arg1","type":"uint256"},{"indexed":true,"name":"arg2","type":"uint256"}],"name":"LogTripleWithIndex","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"arg0","type":"uint256"},{"indexed":false,"name":"arg1","type":"uint256"},{"indexed":true,"name":"arg2","type":"uint256"},{"indexed":true,"name":"arg3","type":"uint256"}],"name":"LogQuadrupleWithIndex","type":"event"}]')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_SOURCE():
|
||||
return CONTRACT_EMITTER_SOURCE
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_CODE():
|
||||
return CONTRACT_EMITTER_CODE
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_RUNTIME():
|
||||
return CONTRACT_EMITTER_RUNTIME
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER_ABI():
|
||||
return CONTRACT_EMITTER_ABI
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def EMITTER(EMITTER_CODE,
|
||||
EMITTER_RUNTIME,
|
||||
EMITTER_ABI,
|
||||
EMITTER_SOURCE):
|
||||
return {
|
||||
'code': EMITTER_CODE,
|
||||
'code_runtime': EMITTER_RUNTIME,
|
||||
'source': EMITTER_SOURCE,
|
||||
'abi': EMITTER_ABI,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def Emitter(web3, EMITTER):
|
||||
return web3.eth.contract(**EMITTER)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def emitter(web3, Emitter, wait_for_transaction, wait_for_block):
|
||||
wait_for_block(web3)
|
||||
deploy_txn_hash = Emitter.deploy({'from': web3.eth.coinbase, 'gas': 1000000})
|
||||
deploy_receipt = wait_for_transaction(deploy_txn_hash)
|
||||
contract_address = deploy_receipt['contractAddress']
|
||||
|
||||
code = web3.eth.getCode(contract_address)
|
||||
assert code == Emitter.code_runtime
|
||||
return Emitter(address=contract_address)
|
||||
|
||||
|
||||
class LogFunctions(object):
|
||||
LogAnonymous = 0
|
||||
LogNoArguments = 1
|
||||
LogSingleArg = 2
|
||||
LogDoubleArg = 3
|
||||
LogTripleArg = 4
|
||||
LogQuadrupleArg = 5
|
||||
LogSingleWithIndex = 6
|
||||
LogDoubleWithIndex = 7
|
||||
LogTripleWithIndex = 8
|
||||
LogQuadrupleWithIndex = 9
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def emitter_event_ids():
|
||||
return LogFunctions
|
||||
|
||||
|
||||
def event_topic(event_signature):
|
||||
from web3.utils.string import force_bytes
|
||||
return force_bytes("0x" + sha3_256(force_bytes(event_signature)).hexdigest())
|
||||
|
||||
|
||||
class LogTopics(object):
|
||||
LogAnonymous = event_topic("LogAnonymous()")
|
||||
LogNoArguments = event_topic("LogNoArguments()")
|
||||
LogSingleArg = event_topic("LogSingleArg(uint256)")
|
||||
LogSingleWithIndex = event_topic("LogSingleWithIndex(uint256)")
|
||||
LogDoubleArg = event_topic("LogDoubleArg(uint256,uint256)")
|
||||
LogDoubleWithIndex = event_topic("LogDoubleWithIndex(uint256,uint256)")
|
||||
LogTripleArg = event_topic("LogTripleArg(uint256,uint256,uint256)")
|
||||
LogTripleWithIndex = event_topic("LogTripleWithIndex(uint256,uint256,uint256)")
|
||||
LogQuadrupleArg = event_topic("LogQuadrupleArg(uint256,uint256,uint256,uint256)")
|
||||
LogQuadrupleWithIndex = event_topic("LogQuadrupleWithIndex(uint256,uint256,uint256,uint256)")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def emitter_log_topics():
|
||||
return LogTopics
|
||||
56
tests/filtering/test_contract_on_event_filtering.py
Normal file
56
tests/filtering/test_contract_on_event_filtering.py
Normal file
@ -0,0 +1,56 @@
|
||||
import random
|
||||
import gevent
|
||||
|
||||
|
||||
def test_on_filter_with_only_event_name(web3,
|
||||
emitter,
|
||||
wait_for_transaction,
|
||||
emitter_log_topics,
|
||||
emitter_event_ids):
|
||||
|
||||
seen_logs = []
|
||||
|
||||
filter = emitter.on('LogNoArguments', {}, seen_logs.append)
|
||||
|
||||
txn_hash = emitter.transact().logNoArgs(emitter_event_ids.LogNoArguments)
|
||||
txn_receipt = wait_for_transaction(txn_hash)
|
||||
|
||||
gevent.sleep(1)
|
||||
|
||||
filter.stop_watching(10)
|
||||
|
||||
assert len(seen_logs) == 1
|
||||
assert seen_logs[0]['transactionHash'] == txn_hash
|
||||
|
||||
|
||||
def test_on_filter_with_event_name_and_single_argument(web3,
|
||||
emitter,
|
||||
wait_for_transaction,
|
||||
emitter_log_topics,
|
||||
emitter_event_ids):
|
||||
|
||||
seen_logs = []
|
||||
|
||||
filter = emitter.on('LogTripleWithIndex', {'filter': {
|
||||
'arg1': 2,
|
||||
}}, seen_logs.append)
|
||||
|
||||
txn_hashes = []
|
||||
txn_hashes.append(
|
||||
emitter.transact().logTriple(emitter_event_ids.LogTripleWithIndex, 2, 1, 3)
|
||||
)
|
||||
txn_hashes.append(
|
||||
emitter.transact().logTriple(emitter_event_ids.LogTripleWithIndex, 1, 2, 3)
|
||||
)
|
||||
txn_hashes.append(
|
||||
emitter.transact().logTriple(emitter_event_ids.LogTripleWithIndex, 12345, 2, 54321)
|
||||
)
|
||||
for txn_hash in txn_hashes:
|
||||
wait_for_transaction(txn_hash)
|
||||
|
||||
gevent.sleep(1)
|
||||
|
||||
filter.stop_watching(10)
|
||||
|
||||
assert len(seen_logs) == 2
|
||||
assert {l['transactionHash'] for l in seen_logs} == set(txn_hashes[1:])
|
||||
24
tests/filtering/test_contract_past_event_filtering.py
Normal file
24
tests/filtering/test_contract_past_event_filtering.py
Normal file
@ -0,0 +1,24 @@
|
||||
import random
|
||||
import gevent
|
||||
|
||||
|
||||
def test_past_events_filter_with_only_event_name(web3,
|
||||
emitter,
|
||||
wait_for_transaction,
|
||||
emitter_log_topics,
|
||||
emitter_event_ids):
|
||||
txn_hash = emitter.transact().logNoArgs(emitter_event_ids.LogNoArguments)
|
||||
txn_receipt = wait_for_transaction(txn_hash)
|
||||
|
||||
seen_logs = []
|
||||
|
||||
filter = emitter.pastEvents('LogNoArguments', {}, seen_logs.append)
|
||||
|
||||
with gevent.Timeout(5):
|
||||
while not seen_logs:
|
||||
gevent.sleep(random.random())
|
||||
|
||||
filter.stop_watching(10)
|
||||
|
||||
assert len(seen_logs) == 1
|
||||
assert seen_logs[0]['transactionHash'] == txn_hash
|
||||
20
tests/filtering/test_filter_against_latest_blocks.py
Normal file
20
tests/filtering/test_filter_against_latest_blocks.py
Normal file
@ -0,0 +1,20 @@
|
||||
def test_filter_against_latest_blocks(web3, wait_for_block):
|
||||
seen_blocks = []
|
||||
txn_filter = web3.eth.filter("latest")
|
||||
txn_filter.watch(seen_blocks.append)
|
||||
|
||||
current_block = web3.eth.blockNumber
|
||||
|
||||
wait_for_block(web3, current_block + 3)
|
||||
|
||||
txn_filter.stop_watching(3)
|
||||
|
||||
# give the gevent threads a moment to catch the latest block.
|
||||
wait_for_block(web3, web3.eth.blockNumber + 1)
|
||||
|
||||
expected_block_hashes = [
|
||||
web3.eth.getBlock(n)['hash'] for n in range(current_block + 1, current_block + 4)
|
||||
]
|
||||
assert len(seen_blocks) > 2
|
||||
|
||||
assert set(expected_block_hashes).issuperset(seen_blocks)
|
||||
23
tests/filtering/test_filter_against_pending_transactions.py
Normal file
23
tests/filtering/test_filter_against_pending_transactions.py
Normal file
@ -0,0 +1,23 @@
|
||||
def test_filter_against_pending_transactions(web3, wait_for_transaction):
|
||||
seen_txns = []
|
||||
txn_filter = web3.eth.filter("pending")
|
||||
txn_filter.watch(seen_txns.append)
|
||||
|
||||
txn_1_hash = web3.eth.sendTransaction({
|
||||
'from': web3.eth.coinbase,
|
||||
'to': '0xd3cda913deb6f67967b99d67acdfa1712c293601',
|
||||
'value': 12345,
|
||||
})
|
||||
txn_2_hash = web3.eth.sendTransaction({
|
||||
'from': web3.eth.coinbase,
|
||||
'to': '0xd3cda913deb6f67967b99d67acdfa1712c293601',
|
||||
'value': 54321,
|
||||
})
|
||||
|
||||
wait_for_transaction(txn_1_hash)
|
||||
wait_for_transaction(txn_2_hash)
|
||||
|
||||
txn_filter.stop_watching(30)
|
||||
|
||||
assert txn_1_hash in seen_txns
|
||||
assert txn_2_hash in seen_txns
|
||||
20
tests/filtering/test_filter_against_transaction_logs.py
Normal file
20
tests/filtering/test_filter_against_transaction_logs.py
Normal file
@ -0,0 +1,20 @@
|
||||
def test_filter_against_log_events(web3,
|
||||
emitter,
|
||||
wait_for_transaction,
|
||||
emitter_log_topics,
|
||||
emitter_event_ids):
|
||||
|
||||
seen_logs = []
|
||||
txn_filter = web3.eth.filter({})
|
||||
txn_filter.watch(seen_logs.append)
|
||||
|
||||
txn_hashes = []
|
||||
|
||||
txn_hashes.append(emitter.transact().logNoArgs(emitter_event_ids.LogNoArguments))
|
||||
|
||||
for txn_hash in txn_hashes:
|
||||
wait_for_transaction(txn_hash)
|
||||
|
||||
txn_filter.stop_watching(30)
|
||||
|
||||
assert set(txn_hashes) == set(log['transactionHash'] for log in seen_logs)
|
||||
61
tests/utilities/test_abi_filtering_by_argument_name.py
Normal file
61
tests/utilities/test_abi_filtering_by_argument_name.py
Normal file
@ -0,0 +1,61 @@
|
||||
import pytest
|
||||
|
||||
from web3.utils.abi import (
|
||||
filter_by_argument_name,
|
||||
)
|
||||
|
||||
ABI = [
|
||||
{
|
||||
"constant": False,
|
||||
"inputs": [],
|
||||
"name": "func_1",
|
||||
"outputs": [],
|
||||
"type": "function",
|
||||
},
|
||||
{
|
||||
"constant": False,
|
||||
"inputs": [
|
||||
{"name": "a", "type": "uint256"},
|
||||
],
|
||||
"name": "func_2",
|
||||
"outputs": [],
|
||||
"type": "function",
|
||||
},
|
||||
{
|
||||
"constant": False,
|
||||
"inputs": [
|
||||
{"name": "a", "type": "uint256"},
|
||||
{"name": "b", "type": "uint256"},
|
||||
],
|
||||
"name": "func_3",
|
||||
"outputs": [],
|
||||
"type": "function",
|
||||
},
|
||||
{
|
||||
"constant": False,
|
||||
"inputs": [
|
||||
{"name": "a", "type": "uint256"},
|
||||
{"name": "b", "type": "uint256"},
|
||||
{"name": "c", "type": "uint256"},
|
||||
],
|
||||
"name": "func_4",
|
||||
"outputs": [],
|
||||
"type": "function",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'argument_names,expected',
|
||||
(
|
||||
([], ['func_1', 'func_2', 'func_3', 'func_4']),
|
||||
(['a'], ['func_2', 'func_3', 'func_4']),
|
||||
(['a', 'c'], ['func_4']),
|
||||
(['c'], ['func_4']),
|
||||
(['b'], ['func_3', 'func_4']),
|
||||
)
|
||||
)
|
||||
def test_filter_by_arguments_1(argument_names, expected):
|
||||
actual_matches = filter_by_argument_name(argument_names, ABI)
|
||||
function_names = [match['name'] for match in actual_matches]
|
||||
assert set(function_names) == set(expected)
|
||||
76
tests/utilities/test_construct_event_data_set.py
Normal file
76
tests/utilities/test_construct_event_data_set.py
Normal file
@ -0,0 +1,76 @@
|
||||
import pytest
|
||||
|
||||
|
||||
from web3.utils.abi import (
|
||||
construct_event_data_set,
|
||||
)
|
||||
|
||||
|
||||
EVENT_1_ABI = {
|
||||
"anonymous": False,
|
||||
"inputs": [
|
||||
{"indexed": False,"name":"arg0","type":"uint256"},
|
||||
{"indexed": True,"name":"arg1","type":"uint256"},
|
||||
{"indexed": True,"name":"arg2","type":"uint256"},
|
||||
{"indexed": False,"name":"arg3","type":"uint256"},
|
||||
{"indexed": True,"name":"arg4","type":"uint256"},
|
||||
{"indexed": False,"name":"arg5","type":"uint256"},
|
||||
],
|
||||
"name": "Event_1",
|
||||
"type":"event",
|
||||
}
|
||||
EVENT_1_TOPIC = '0xa7144ed450ecab4a6283d3b1e290ff6c889232d922b84d88203eb7619222fb32'
|
||||
|
||||
|
||||
def hex_and_pad(i):
|
||||
unpadded_hex_value = hex(i)
|
||||
return '0x' + unpadded_hex_value[2:].zfill(65 - len(unpadded_hex_value[2:]))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'event_abi,arguments,expected',
|
||||
(
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{},
|
||||
[[]],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg1': 1},
|
||||
[[]],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg0': 1},
|
||||
[[hex_and_pad(1), None, None]],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg0': [1]},
|
||||
[[hex_and_pad(1), None, None]],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg0': [1, 2]},
|
||||
[
|
||||
[hex_and_pad(1), None, None],
|
||||
[hex_and_pad(2), None, None],
|
||||
],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg0': [1, 3], 'arg3': [2, 4]},
|
||||
[
|
||||
[hex_and_pad(1), hex_and_pad(2), None],
|
||||
[hex_and_pad(1), hex_and_pad(4), None],
|
||||
[hex_and_pad(3), hex_and_pad(2), None],
|
||||
[hex_and_pad(3), hex_and_pad(4), None],
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
def test_construct_event_data_set(event_abi, arguments, expected):
|
||||
actual = construct_event_data_set(event_abi, arguments)
|
||||
assert actual == expected
|
||||
|
||||
55
tests/utilities/test_construct_event_filter_params.py
Normal file
55
tests/utilities/test_construct_event_filter_params.py
Normal file
@ -0,0 +1,55 @@
|
||||
import pytest
|
||||
|
||||
from web3.utils.abi import (
|
||||
event_abi_to_log_topic,
|
||||
)
|
||||
from web3.utils.filters import (
|
||||
construct_event_filter_params,
|
||||
)
|
||||
|
||||
EVENT_1_ABI = {
|
||||
"anonymous": False,
|
||||
"inputs": [
|
||||
{"indexed": False,"name":"arg0","type":"uint256"},
|
||||
{"indexed": True,"name":"arg1","type":"uint256"},
|
||||
],
|
||||
"name": "Event_1",
|
||||
"type":"event",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"event_abi,fn_kwargs,expected",
|
||||
(
|
||||
(EVENT_1_ABI, {}, {
|
||||
"topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'],
|
||||
}),
|
||||
(EVENT_1_ABI, {'topics': ['should-be-preserved']}, {
|
||||
"topics": [
|
||||
['should-be-preserved'],
|
||||
['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'],
|
||||
]
|
||||
}),
|
||||
(EVENT_1_ABI, {'contract_address': '0xd3cda913deb6f67967b99d67acdfa1712c293601'}, {
|
||||
"topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'],
|
||||
'address': '0xd3cda913deb6f67967b99d67acdfa1712c293601',
|
||||
}),
|
||||
(EVENT_1_ABI, {
|
||||
'contract_address': '0xd3cda913deb6f67967b99d67acdfa1712c293601',
|
||||
'address': '0xbb9bc244d798123fde783fcc1c72d3bb8c189413',
|
||||
}, {
|
||||
"topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'],
|
||||
'address': [
|
||||
'0xbb9bc244d798123fde783fcc1c72d3bb8c189413',
|
||||
'0xd3cda913deb6f67967b99d67acdfa1712c293601',
|
||||
],
|
||||
}),
|
||||
(EVENT_1_ABI, {'address': '0xd3cda913deb6f67967b99d67acdfa1712c293601'}, {
|
||||
"topics": ['0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6'],
|
||||
'address': '0xd3cda913deb6f67967b99d67acdfa1712c293601',
|
||||
}),
|
||||
),
|
||||
)
|
||||
def test_construct_event_filter_params(event_abi, fn_kwargs, expected):
|
||||
actual = construct_event_filter_params(event_abi, **fn_kwargs)
|
||||
assert actual == expected
|
||||
84
tests/utilities/test_construct_event_topics.py
Normal file
84
tests/utilities/test_construct_event_topics.py
Normal file
@ -0,0 +1,84 @@
|
||||
import pytest
|
||||
|
||||
|
||||
from web3.utils.abi import (
|
||||
construct_event_topic_set,
|
||||
)
|
||||
|
||||
|
||||
EVENT_1_ABI = {
|
||||
"anonymous": False,
|
||||
"inputs": [
|
||||
{"indexed": False,"name":"arg0","type":"uint256"},
|
||||
{"indexed": True,"name":"arg1","type":"uint256"},
|
||||
{"indexed": True,"name":"arg2","type":"uint256"},
|
||||
{"indexed": False,"name":"arg3","type":"uint256"},
|
||||
{"indexed": True,"name":"arg4","type":"uint256"},
|
||||
{"indexed": False,"name":"arg5","type":"uint256"},
|
||||
],
|
||||
"name": "Event_1",
|
||||
"type":"event",
|
||||
}
|
||||
EVENT_1_TOPIC = '0xa7144ed450ecab4a6283d3b1e290ff6c889232d922b84d88203eb7619222fb32'
|
||||
|
||||
|
||||
def hex_and_pad(i):
|
||||
unpadded_hex_value = hex(i)
|
||||
return '0x' + unpadded_hex_value[2:].zfill(65 - len(unpadded_hex_value[2:]))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'event_abi,arguments,expected',
|
||||
(
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{},
|
||||
[[EVENT_1_TOPIC]],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg0': 1},
|
||||
[[EVENT_1_TOPIC]],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg0': 1, 'arg3': [1, 2]},
|
||||
[[EVENT_1_TOPIC]],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg1': 1},
|
||||
[
|
||||
[EVENT_1_TOPIC, hex_and_pad(1), None, None],
|
||||
],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg1': [1, 2]},
|
||||
[
|
||||
[EVENT_1_TOPIC, hex_and_pad(1), None, None],
|
||||
[EVENT_1_TOPIC, hex_and_pad(2), None, None],
|
||||
],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg1': [1], 'arg2': [2]},
|
||||
[
|
||||
[EVENT_1_TOPIC, hex_and_pad(1), hex_and_pad(2), None],
|
||||
],
|
||||
),
|
||||
(
|
||||
EVENT_1_ABI,
|
||||
{'arg1': [1, 3], 'arg2': [2, 4]},
|
||||
[
|
||||
[EVENT_1_TOPIC, hex_and_pad(1), hex_and_pad(2), None],
|
||||
[EVENT_1_TOPIC, hex_and_pad(1), hex_and_pad(4), None],
|
||||
[EVENT_1_TOPIC, hex_and_pad(3), hex_and_pad(2), None],
|
||||
[EVENT_1_TOPIC, hex_and_pad(3), hex_and_pad(4), None],
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
def test_construct_event_topics(event_abi, arguments, expected):
|
||||
actual = construct_event_topic_set(event_abi, arguments)
|
||||
assert actual == expected
|
||||
@ -16,6 +16,9 @@ from web3.utils.formatting import (
|
||||
add_0x_prefix,
|
||||
remove_0x_prefix,
|
||||
)
|
||||
from web3.utils.types import (
|
||||
is_array,
|
||||
)
|
||||
from web3.utils.string import (
|
||||
force_bytes,
|
||||
coerce_return_to_text,
|
||||
@ -25,12 +28,21 @@ from web3.utils.abi import (
|
||||
filter_by_type,
|
||||
filter_by_name,
|
||||
filter_by_argument_count,
|
||||
filter_by_argument_name,
|
||||
filter_by_encodability,
|
||||
get_abi_input_types,
|
||||
get_abi_output_types,
|
||||
get_constructor_abi,
|
||||
check_if_arguments_can_be_encoded,
|
||||
function_abi_to_4byte_selector,
|
||||
event_abi_to_log_topic,
|
||||
)
|
||||
from web3.utils.functional import (
|
||||
compose,
|
||||
)
|
||||
from web3.utils.filters import (
|
||||
construct_event_filter_params,
|
||||
LogFilter,
|
||||
)
|
||||
|
||||
|
||||
@ -232,7 +244,7 @@ class Contract(object):
|
||||
# ABI Helpers
|
||||
#
|
||||
@classmethod
|
||||
def find_matching_abi(cls, fn_name, arguments):
|
||||
def find_matching_fn_abi(cls, fn_name, arguments):
|
||||
filters = [
|
||||
functools.partial(filter_by_name, fn_name),
|
||||
functools.partial(filter_by_argument_count, arguments),
|
||||
@ -254,13 +266,30 @@ class Contract(object):
|
||||
else:
|
||||
raise ValueError("Multiple functions found")
|
||||
|
||||
@classmethod
|
||||
def find_matching_event_abi(cls, event_name, argument_names):
|
||||
filter_fn = compose(
|
||||
functools.partial(filter_by_type, 'event'),
|
||||
functools.partial(filter_by_name, event_name),
|
||||
functools.partial(filter_by_argument_name, argument_names),
|
||||
)
|
||||
|
||||
event_abi_candidates = filter_fn(cls.abi)
|
||||
|
||||
if len(event_abi_candidates) == 1:
|
||||
return event_abi_candidates[0]
|
||||
elif not event_abi_candidates:
|
||||
raise ValueError("No matching functions found")
|
||||
else:
|
||||
raise ValueError("Multiple functions found")
|
||||
|
||||
@classmethod
|
||||
@coerce_return_to_text
|
||||
def encodeABI(cls, fn_name, arguments, data=None):
|
||||
"""
|
||||
encodes the arguments using the Ethereum ABI.
|
||||
"""
|
||||
function_abi = cls.find_matching_abi(fn_name, force_obj_to_bytes(arguments))
|
||||
function_abi = cls.find_matching_fn_abi(fn_name, force_obj_to_bytes(arguments))
|
||||
return cls._encodeABI(function_abi, arguments, data)
|
||||
|
||||
@classmethod
|
||||
@ -309,17 +338,61 @@ class Contract(object):
|
||||
|
||||
return deploy_data
|
||||
|
||||
def on(self, event, filters, callback):
|
||||
def on(self, event_name, default_filter_params=None, *callbacks):
|
||||
"""
|
||||
register a callback to be triggered on the appropriate events.
|
||||
"""
|
||||
raise NotImplementedError('Not implemented')
|
||||
if default_filter_params is None:
|
||||
default_filter_params = {}
|
||||
|
||||
def pastEvents(self, event, filters, callback):
|
||||
argument_filters = default_filter_params.pop('filter', {})
|
||||
argument_filter_names = list(argument_filters.keys())
|
||||
event_abi = self.find_matching_event_abi(event_name, argument_filter_names)
|
||||
|
||||
filter_params = construct_event_filter_params(
|
||||
event_abi,
|
||||
contract_address=self.address,
|
||||
argument_filters=argument_filters,
|
||||
**default_filter_params
|
||||
)
|
||||
|
||||
filter = self.web3.eth.filter(filter_params)
|
||||
|
||||
if callbacks:
|
||||
filter.watch(*callbacks)
|
||||
|
||||
filter.filter_params = filter_params
|
||||
return filter
|
||||
|
||||
def pastEvents(self, event_name, default_filter_params=None, *callbacks):
|
||||
"""
|
||||
register a callback to be triggered on all past events.
|
||||
"""
|
||||
raise NotImplementedError('Not implemented')
|
||||
if default_filter_params is None:
|
||||
default_filter_params = {}
|
||||
|
||||
if 'fromBlock' in default_filter_params or 'toBlock' in default_filter_params:
|
||||
raise ValueError("Cannot provide `fromBlock` or `toBlock` in `pastEvents` calls")
|
||||
|
||||
argument_filters = default_filter_params.pop('filter', {})
|
||||
argument_filter_names = list(argument_filters.keys())
|
||||
event_abi = self.find_matching_event_abi(event_name, argument_filter_names)
|
||||
|
||||
filter_params = construct_event_filter_params(
|
||||
event_abi,
|
||||
contract_address=self.address,
|
||||
argument_filters=argument_filters,
|
||||
fromBlock="earliest",
|
||||
toBlock=self.web3.eth.blockNumber,
|
||||
**default_filter_params
|
||||
)
|
||||
|
||||
filter = self.web3.eth.filter(filter_params)
|
||||
|
||||
if callbacks:
|
||||
filter.watch(*callbacks)
|
||||
|
||||
return filter
|
||||
|
||||
def estimateGas(self, transaction=None):
|
||||
"""
|
||||
@ -506,7 +579,7 @@ def call_contract_function(contract=None,
|
||||
if not arguments:
|
||||
arguments = []
|
||||
|
||||
function_abi = contract.find_matching_abi(function_name, arguments)
|
||||
function_abi = contract.find_matching_fn_abi(function_name, arguments)
|
||||
function_selector = function_abi_to_4byte_selector(function_abi)
|
||||
|
||||
transaction['data'] = contract.encodeABI(
|
||||
@ -590,7 +663,7 @@ def transact_with_contract_function(contract=None,
|
||||
if not arguments:
|
||||
arguments = []
|
||||
|
||||
function_abi = contract.find_matching_abi(function_name, arguments)
|
||||
function_abi = contract.find_matching_fn_abi(function_name, arguments)
|
||||
function_selector = function_abi_to_4byte_selector(function_abi)
|
||||
|
||||
transaction['data'] = contract.encodeABI(
|
||||
@ -615,7 +688,7 @@ def estimate_gas_for_function(contract=None,
|
||||
if not arguments:
|
||||
arguments = []
|
||||
|
||||
function_abi = contract.find_matching_abi(function_name, arguments)
|
||||
function_abi = contract.find_matching_fn_abi(function_name, arguments)
|
||||
function_selector = function_abi_to_4byte_selector(function_abi)
|
||||
|
||||
transaction['data'] = contract.encodeABI(
|
||||
|
||||
42
web3/eth.py
42
web3/eth.py
@ -8,10 +8,16 @@ from web3.utils.encoding import (
|
||||
)
|
||||
from web3.utils.types import (
|
||||
is_integer,
|
||||
is_string,
|
||||
)
|
||||
from web3.utils.functional import (
|
||||
apply_formatters_to_return,
|
||||
)
|
||||
from web3.utils.filters import (
|
||||
BlockFilter,
|
||||
TransactionFilter,
|
||||
LogFilter,
|
||||
)
|
||||
from web3.contract import construct_contract_class
|
||||
|
||||
|
||||
@ -218,13 +224,35 @@ class Eth(object):
|
||||
def estimateGas(self, transaction):
|
||||
return self.request_manager.request_blocking("eth_estimateGas", [transaction])
|
||||
|
||||
def filter(self, *args, **kwargs):
|
||||
"""
|
||||
`eth_newFilter`
|
||||
`eth_newBlockFilter`
|
||||
`eth_uninstallFilter`
|
||||
"""
|
||||
raise NotImplementedError("TODO")
|
||||
def filter(self, filter_params):
|
||||
if is_string(filter_params):
|
||||
if filter_params == "latest":
|
||||
filter_id = self.request_manager.request_blocking("eth_newBlockFilter", [])
|
||||
return BlockFilter(self.web3, filter_id)
|
||||
elif filter_params == "pending":
|
||||
filter_id = self.request_manager.request_blocking(
|
||||
"eth_newPendingTransactionFilter", [],
|
||||
)
|
||||
return TransactionFilter(self.web3, filter_id)
|
||||
else:
|
||||
raise ValueError(
|
||||
"The filter API only accepts the values of `pending` or "
|
||||
"`latest` for string based filters"
|
||||
)
|
||||
elif isinstance(filter_params, dict):
|
||||
filter_id = self.request_manager.request_blocking("eth_newFilter", [filter_params])
|
||||
return LogFilter(self.web3, filter_id)
|
||||
else:
|
||||
raise ValueError("Must provide either a string or a valid filter object")
|
||||
|
||||
def getFilterChanges(self, filter_id):
|
||||
return self.request_manager.request_blocking("eth_getFilterChanges", [filter_id])
|
||||
|
||||
def getFilterLogs(self, filter_id):
|
||||
return self.request_manager.request_blocking("eth_getFilterLogs", [filter_id])
|
||||
|
||||
def uninstallFilter(self, filter_id):
|
||||
return self.request_manager.request_blocking("eth_uninstallFilter", [filter_id])
|
||||
|
||||
def contract(self, abi, address=None, **kwargs):
|
||||
contract_class = construct_contract_class(self.web3, abi, **kwargs)
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
import itertools
|
||||
|
||||
from eth_abi.abi import (
|
||||
process_type,
|
||||
encode_single,
|
||||
)
|
||||
|
||||
from .encoding import encode_hex
|
||||
from .crypto import sha3
|
||||
from .string import coerce_args_to_bytes
|
||||
from .string import (
|
||||
coerce_args_to_bytes,
|
||||
coerce_return_to_text,
|
||||
)
|
||||
from .formatting import (
|
||||
add_0x_prefix,
|
||||
)
|
||||
@ -34,6 +41,18 @@ def get_abi_output_types(abi):
|
||||
return [arg['type'] for arg in abi['outputs']]
|
||||
|
||||
|
||||
def get_abi_input_names(abi):
|
||||
return [arg['name'] for arg in abi['inputs']]
|
||||
|
||||
|
||||
def get_indexed_event_inputs(event_abi):
|
||||
return [arg for arg in event_abi['inputs'] if arg['indexed'] is True]
|
||||
|
||||
|
||||
def exclude_indexed_event_inputs(event_abi):
|
||||
return [arg for arg in event_abi['inputs'] if arg['indexed'] is False]
|
||||
|
||||
|
||||
def filter_by_argument_count(arguments, contract_abi):
|
||||
return [
|
||||
abi
|
||||
@ -43,6 +62,16 @@ def filter_by_argument_count(arguments, contract_abi):
|
||||
]
|
||||
|
||||
|
||||
def filter_by_argument_name(argument_names, contract_abi):
|
||||
return [
|
||||
abi
|
||||
for abi in contract_abi
|
||||
if set(argument_names).intersection(
|
||||
get_abi_input_names(abi)
|
||||
) == set(argument_names)
|
||||
]
|
||||
|
||||
|
||||
def is_encodable(_type, value):
|
||||
try:
|
||||
base, sub, arrlist = _type
|
||||
@ -124,7 +153,7 @@ def get_constructor_abi(contract_abi):
|
||||
raise ValueError("Found multiple constructors.")
|
||||
|
||||
|
||||
def abi_to_4byte_function_selector(function_abi):
|
||||
def abi_to_signature(function_abi):
|
||||
function_signature = "{fn_name}({fn_input_types})".format(
|
||||
fn_name=function_abi['name'],
|
||||
fn_input_types=','.join([
|
||||
@ -135,5 +164,95 @@ def abi_to_4byte_function_selector(function_abi):
|
||||
|
||||
|
||||
def function_abi_to_4byte_selector(function_abi):
|
||||
function_signature = abi_to_4byte_function_selector(function_abi)
|
||||
function_signature = abi_to_signature(function_abi)
|
||||
return add_0x_prefix(sha3(function_signature)[:8])
|
||||
|
||||
|
||||
def event_abi_to_log_topic(event_abi):
|
||||
event_signature = abi_to_signature(event_abi)
|
||||
return add_0x_prefix(sha3(event_signature))
|
||||
|
||||
|
||||
@coerce_return_to_text
|
||||
def construct_event_topic_set(event_abi, arguments=None):
|
||||
if arguments is None:
|
||||
arguments = {}
|
||||
if isinstance(arguments, (list, tuple)):
|
||||
if len(arguments) != len(event_abi['inputs']):
|
||||
raise ValueError(
|
||||
"When passing an argument list, the number of arguments must "
|
||||
"match the event constructor."
|
||||
)
|
||||
arguments = {
|
||||
arg['name']: [arg_value]
|
||||
for arg, arg_value
|
||||
in zip(event_abi['inputs'], arguments)
|
||||
}
|
||||
|
||||
normalized_args = {
|
||||
key: value if is_array(value) else [value]
|
||||
for key, value in arguments.items()
|
||||
}
|
||||
|
||||
event_topic = event_abi_to_log_topic(event_abi)
|
||||
indexed_args = get_indexed_event_inputs(event_abi)
|
||||
zipped_abi_and_args = [
|
||||
(arg, normalized_args.get(arg['name'], [None]))
|
||||
for arg in indexed_args
|
||||
]
|
||||
encoded_args = [
|
||||
[
|
||||
None if option is None else encode_hex(encode_single(arg['type'], option))
|
||||
for option in arg_options]
|
||||
for arg, arg_options in zipped_abi_and_args
|
||||
]
|
||||
|
||||
topics = [
|
||||
[event_topic] + list(permutation)
|
||||
if any(value is not None for value in permutation)
|
||||
else [event_topic]
|
||||
for permutation in itertools.product(*encoded_args)
|
||||
]
|
||||
return topics
|
||||
|
||||
|
||||
@coerce_return_to_text
|
||||
def construct_event_data_set(event_abi, arguments=None):
|
||||
if arguments is None:
|
||||
arguments = {}
|
||||
if isinstance(arguments, (list, tuple)):
|
||||
if len(arguments) != len(event_abi['inputs']):
|
||||
raise ValueError(
|
||||
"When passing an argument list, the number of arguments must "
|
||||
"match the event constructor."
|
||||
)
|
||||
arguments = {
|
||||
arg['name']: [arg_value]
|
||||
for arg, arg_value
|
||||
in zip(event_abi['inputs'], arguments)
|
||||
}
|
||||
|
||||
normalized_args = {
|
||||
key: value if is_array(value) else [value]
|
||||
for key, value in arguments.items()
|
||||
}
|
||||
|
||||
indexed_args = exclude_indexed_event_inputs(event_abi)
|
||||
zipped_abi_and_args = [
|
||||
(arg, normalized_args.get(arg['name'], [None]))
|
||||
for arg in indexed_args
|
||||
]
|
||||
encoded_args = [
|
||||
[
|
||||
None if option is None else encode_hex(encode_single(arg['type'], option))
|
||||
for option in arg_options]
|
||||
for arg, arg_options in zipped_abi_and_args
|
||||
]
|
||||
|
||||
topics = [
|
||||
list(permutation)
|
||||
if any(value is not None for value in permutation)
|
||||
else []
|
||||
for permutation in itertools.product(*encoded_args)
|
||||
]
|
||||
return topics
|
||||
|
||||
123
web3/utils/filters.py
Normal file
123
web3/utils/filters.py
Normal file
@ -0,0 +1,123 @@
|
||||
import random
|
||||
import gevent
|
||||
|
||||
from .types import (
|
||||
is_string,
|
||||
is_array,
|
||||
)
|
||||
from .abi import (
|
||||
construct_event_topic_set,
|
||||
construct_event_data_set,
|
||||
)
|
||||
|
||||
|
||||
def construct_event_filter_params(event_abi,
|
||||
contract_address=None,
|
||||
argument_filters=None,
|
||||
topics=None,
|
||||
fromBlock=None,
|
||||
toBlock=None,
|
||||
address=None):
|
||||
filter_params = {}
|
||||
|
||||
if topics is None:
|
||||
topic_set = construct_event_topic_set(event_abi, argument_filters)
|
||||
else:
|
||||
topic_set = [topics] + construct_event_topic_set(event_abi, argument_filters)
|
||||
|
||||
if len(topic_set) == 1 and is_array(topic_set[0]):
|
||||
filter_params['topics'] = topic_set[0]
|
||||
else:
|
||||
filter_params['topics'] = topic_set
|
||||
|
||||
if address and contract_address:
|
||||
if is_array(address):
|
||||
filter_params['address'] = address + [contract_address]
|
||||
elif is_string(address):
|
||||
filter_params['address'] = [address, contract_address]
|
||||
else:
|
||||
raise ValueError(
|
||||
"Unsupported type for `address` parameter: {0}".format(type(address))
|
||||
)
|
||||
elif address:
|
||||
filter_params['address'] = address
|
||||
elif contract_address:
|
||||
filter_params['address'] = contract_address
|
||||
|
||||
if fromBlock is not None:
|
||||
filter_params['fromBlock'] = fromBlock
|
||||
|
||||
if toBlock is not None:
|
||||
filter_params['toBlock'] = toBlock
|
||||
|
||||
return filter_params
|
||||
|
||||
|
||||
class BaseFilter(gevent.Greenlet):
|
||||
callbacks = None
|
||||
running = None
|
||||
stopped = False
|
||||
|
||||
def __init__(self, web3, filter_id):
|
||||
self.web3 = web3
|
||||
self.filter_id = filter_id
|
||||
self.callbacks = []
|
||||
gevent.Greenlet.__init__(self)
|
||||
|
||||
def __str__(self):
|
||||
return "Filter for {0}".format(self.filter_id)
|
||||
|
||||
def _run(self):
|
||||
if self.stopped:
|
||||
raise ValueError("Cannot restart a Filter")
|
||||
self.running = True
|
||||
|
||||
previous_logs = self.web3.eth.getFilterLogs(self.filter_id)
|
||||
if previous_logs:
|
||||
for log in previous_logs:
|
||||
for callback_fn in self.callbacks:
|
||||
callback_fn(log)
|
||||
|
||||
while self.running:
|
||||
changes = self.web3.eth.getFilterChanges(self.filter_id)
|
||||
if changes:
|
||||
for log in changes:
|
||||
for callback_fn in self.callbacks:
|
||||
callback_fn(log)
|
||||
gevent.sleep(random.random())
|
||||
|
||||
def watch(self, *callbacks):
|
||||
if self.stopped:
|
||||
raise ValueError("Cannot watch on a filter that has been stopped")
|
||||
self.callbacks.extend(callbacks)
|
||||
|
||||
if not self.running:
|
||||
self.start()
|
||||
|
||||
def stop_watching(self, timeout=0):
|
||||
self.running = False
|
||||
self.stopped = True
|
||||
self.web3.eth.uninstallFilter(self.filter_id)
|
||||
self.join(timeout)
|
||||
|
||||
stopWatching = stop_watching
|
||||
|
||||
|
||||
class BlockFilter(BaseFilter):
|
||||
pass
|
||||
|
||||
|
||||
class TransactionFilter(BaseFilter):
|
||||
pass
|
||||
|
||||
|
||||
class LogFilter(BaseFilter):
|
||||
def get(self, only_changes=True):
|
||||
if self.running:
|
||||
raise ValueError(
|
||||
"Cannot call `get` on a filter object which is actively watching"
|
||||
)
|
||||
if only_changes:
|
||||
return self.web3.eth.getFilterChanges(self.filter_id)
|
||||
else:
|
||||
return self.web3.eth.getFilterChanges(self.filter_id)
|
||||
Loading…
Reference in New Issue
Block a user