Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #47

Merged
merged 11 commits into from
Nov 15, 2024
Merged
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.3.0
current_version = 1.4.0-beta.1
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-(?P<stage>[^.]*)\.(?P<devnum>\d+))?
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: coverage

on:
pull_request:
push:
branches: ["v1"]

jobs:
Expand All @@ -22,7 +22,7 @@ jobs:
export USE_TESTNET=1 &&
export ENS_ACCOUNT_NAME=${{secrets.ENS_ACCOUNT_NAME}} &&
export ENS_ACCOUNT_SECRET=${{secrets.ENS_ACCOUNT_SECRET}} &&
pytest --cov tests
pytest --cov tests -n 3 --dist loadgroup
- name: Upload coverage reports to Codecov
run: |
curl -Os https://uploader.codecov.io/latest/linux/codecov
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/local-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Python package

on:
pull_request:
branches: [ "dev", "v1"]

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
pip install ".[tester]"
- name: Run test
run: |
pytest tests -n 3 --dist loadgroup
15 changes: 5 additions & 10 deletions .github/workflows/test.yml → .github/workflows/testnet-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
name: Python package

on:
push:
branches: [ "dev", "v1", "cns"]
pull_request:
branches: [ "dev", "v1"]

jobs:
build:
Expand All @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.10", "3.11", "3.12", "3.13"] # 3.9 is tested in coverage
python-version: ["3.13"]

steps:
- uses: actions/checkout@v3
Expand All @@ -27,15 +27,10 @@ jobs:
run: |
pip install ".[tester]"
- name: Run test
run: |
pytest tests
- name: Run test on testnet
run: |
export TESTNET_URL=${{secrets.TESTNET_URL}} &&
export USE_TESTNET=1 &&
export ENS_ACCOUNT_NAME=${{secrets.ENS_ACCOUNT_NAME}} &&
export ENS_ACCOUNT_SECRET=${{secrets.ENS_ACCOUNT_SECRET}} &&
pytest tests
- name: test finalization api
run: |
export USE_TESTNET=1 && export TEST_FINALIZATION=1 && pytest tests/middleware/test_pending.py
export TEST_FINALIZATION=1 &&
pytest tests -n 3 --dist loadgroup
2 changes: 1 addition & 1 deletion cns/cns.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def from_web3(cls, w3: "Web3", addr: Optional[Base32Address] = None) -> Self:
provided, defaults to the mainnet ENS registry address.
"""
provider = w3.manager.provider
middlewares = w3.middleware_onion.middlewares
middlewares = w3.middleware_onion.middleware
default_account = w3.cfx.default_account
return cls(cast("BaseProvider", provider), addr=addr, middlewares=middlewares, default_account=default_account)

Expand Down
23 changes: 21 additions & 2 deletions cns/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Sequence,
Tuple,
cast,
Collection,
)

from ens.utils import (
Expand All @@ -13,6 +14,10 @@
ACCEPTABLE_STALE_HOURS,
)

from web3.exceptions import (
Web3ValueError,
)
from web3.middleware.stalecheck import StalecheckMiddlewareBuilder
from cfx_address import (
Base32Address
)
Expand Down Expand Up @@ -43,16 +48,30 @@ def init_web3(
w3.cfx._default_account = default_account
return customize_web3(w3)

def build_stalecheck_middleware(allowable_delay: int, skip_stalecheck_for_methods: Collection[str]):
def inner(w3):
if allowable_delay <= 0:
raise Web3ValueError(
"You must set a positive allowable_delay in seconds for this middleware"
)
middleware = StalecheckMiddlewareBuilder(w3)
middleware.allowable_delay = allowable_delay
middleware.skip_stalecheck_for_methods = skip_stalecheck_for_methods
middleware.cache = {"latest": None}
return middleware
return inner


def customize_web3(w3: "_Web3") -> "_Web3":
from web3.middleware.stalecheck import make_stalecheck_middleware

from web3.middleware.stalecheck import StalecheckMiddlewareBuilder

if w3.middleware_onion.get("name_to_address"):
w3.middleware_onion.remove("name_to_address")

if not w3.middleware_onion.get("stalecheck"):
w3.middleware_onion.add(
make_stalecheck_middleware(ACCEPTABLE_STALE_HOURS * 3600, ("cfx_getBlockByEpochNumber",)), name="stalecheck"
build_stalecheck_middleware(ACCEPTABLE_STALE_HOURS * 3600, ["cfx_getBlockByEpochNumber"]), name="stalecheck"
)
return w3

Expand Down
12 changes: 8 additions & 4 deletions conflux_web3/_utils/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@
from eth_utils.hexadecimal import encode_hex
from eth_utils.conversions import to_hex

from web3.types import ABIFunction
from eth_typing import ABIFunction
from eth_utils.abi import (
get_abi_input_types,
)
from web3._utils import contracts
from web3._utils.abi import (
from web3.utils.abi import (
check_if_arguments_can_be_encoded,
get_abi_input_types,
)
from web3._utils.abi import (
map_abi_data,
)
from web3._utils.normalizers import (
Expand All @@ -41,7 +45,7 @@ def cfx_encode_abi(
"""
argument_types = get_abi_input_types(abi)

if not check_if_arguments_can_be_encoded(abi, web3.codec, arguments, {}):
if not check_if_arguments_can_be_encoded(abi, *arguments, abi_codec=web3.codec):
raise TypeError(
"One or more arguments could not be encoded to the necessary "
"ABI type. Expected types are: {0}".format(
Expand Down
91 changes: 49 additions & 42 deletions conflux_web3/_utils/events.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
from typing import (
Optional,
cast,
Union,
Any,
)
import itertools

from eth_utils.abi import (
get_abi_input_names,
)
from eth_abi.codec import (
ABICodec,
)
from eth_utils.abi import (
event_abi_to_log_topic,
)

from eth_utils.conversions import (
to_bytes,
)
from eth_utils.toolz import (
curry, # type: ignore
)
from web3._utils import events

from web3._utils.events import (
get_event_abi_types_for_decoding
get_event_abi_types_for_decoding,
)
from web3._utils.abi import (
exclude_indexed_event_inputs,
get_abi_input_names,
get_indexed_event_inputs,
map_abi_data,
normalize_event_input_types,
named_tree
)
from web3.types import (
from web3.utils.abi import (
get_event_log_topics,
)
from eth_typing import (
ABIEvent,
)
from web3.datastructures import (
Expand All @@ -37,7 +41,6 @@
from web3.exceptions import (
InvalidEventABI,
LogTopicError,
MismatchedABI,
)
from web3._utils.encoding import (
hexstr_if_str,
Expand All @@ -49,44 +52,49 @@
from conflux_web3.types import (
EventData,
LogReceipt,
TransactionLogReceipt
)

def _log_entry_data_to_bytes(
log_entry_data: Any,
):
return hexstr_if_str(to_bytes, log_entry_data) # type: ignore

def get_cfx_base32_normalizer(chain_id: int): # type: ignore
return lambda type_str, hex_address: (type_str, normalize_to(hex_address, chain_id, True)) if type_str == "address" else (type_str, hex_address) # type: ignore

@curry

@curry # type: ignore
def cfx_get_event_data(
abi_codec: ABICodec, event_abi: ABIEvent, log_entry: Union[TransactionLogReceipt, LogReceipt], chain_id: Optional[int]= None
abi_codec: ABICodec,
event_abi: ABIEvent,
log_entry: LogReceipt,
chain_id: Optional[int]= None
) -> EventData:
"""
Given an event ABI and a log entry for that event, return the decoded
event data.
Modified from web3._utils.events.get_event_data
event data
"""
if event_abi.get("anonymous", None):
log_topics = log_entry["topics"]
elif not log_entry["topics"]:
raise MismatchedABI("Expected non-anonymous event to have 1 or more topics")
# type ignored b/c event_abi_to_log_topic(event_abi: Dict[str, Any])
elif event_abi_to_log_topic(event_abi) != log_entry["topics"][0]: # type: ignore
raise MismatchedABI("The event signature did not match the provided ABI")
else:
log_topics = log_entry["topics"][1:]

log_topics = get_event_log_topics(event_abi, log_entry["topics"])
log_topics_bytes = [_log_entry_data_to_bytes(topic) for topic in log_topics]
log_topics_abi = get_indexed_event_inputs(event_abi)
log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi)
log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs)
log_topic_names = get_abi_input_names(ABIEvent({"inputs": log_topics_abi}))
log_topic_names = get_abi_input_names(
ABIEvent({"name": event_abi["name"], "type": "event", "inputs": log_topics_abi})
)

if len(log_topics) != len(log_topic_types):
if len(log_topics_bytes) != len(log_topic_types):
raise LogTopicError(
f"Expected {len(log_topic_types)} log topics. Got {len(log_topics)}"
f"Expected {len(log_topic_types)} log topics. Got {len(log_topics_bytes)}"
)

log_data = hexstr_if_str(to_bytes, log_entry["data"])
log_data = _log_entry_data_to_bytes(log_entry["data"])
log_data_abi = exclude_indexed_event_inputs(event_abi)
log_data_normalized_inputs = normalize_event_input_types(log_data_abi)
log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs)
log_data_names = get_abi_input_names(ABIEvent({"inputs": log_data_abi}))
log_data_names = get_abi_input_names(
ABIEvent({"name": event_abi["name"], "type": "event", "inputs": log_data_abi})
)

# sanity check that there are not name intersections between the topic
# names and the data argument names.
Expand All @@ -98,28 +106,26 @@ def cfx_get_event_data(
)

decoded_log_data = abi_codec.decode(log_data_types, log_data)

return_normalizer = [
lambda type_str, hex_address: (type_str, normalize_to(hex_address, chain_id, True)) if type_str == "address" \
else (type_str, hex_address)
]

normalized_log_data = map_abi_data(
return_normalizer, log_data_types, decoded_log_data
[get_cfx_base32_normalizer(chain_id)], log_data_types, decoded_log_data
)
named_log_data = named_tree(
log_data_normalized_inputs,
normalized_log_data,
)

decoded_topic_data = [
abi_codec.decode([topic_type], topic_data)[0]
for topic_type, topic_data in zip(log_topic_types, log_topics)
for topic_type, topic_data in zip(log_topic_types, log_topics_bytes)
]
normalized_topic_data = map_abi_data(
return_normalizer, log_topic_types, decoded_topic_data
[get_cfx_base32_normalizer(chain_id)], log_topic_types, decoded_topic_data
)

event_args = dict(
itertools.chain(
zip(log_topic_names, normalized_topic_data),
zip(log_data_names, normalized_log_data),
named_log_data.items(),
)
)

Expand All @@ -132,11 +138,12 @@ def cfx_get_event_data(
"transactionHash": log_entry.get("transactionHash", None),
"address": log_entry["address"],
"blockHash": log_entry.get("blockHash", None),
"epochNumber": log_entry.get("epochNumber", None),
"epochNumber": log_entry.get("epochNumber", None)
}
if isinstance(log_entry, AttributeDict):
return cast(EventData, AttributeDict.recursive(event_data))

return cast(EventData, AttributeDict.recursive(event_data))

return event_data


# events.get_event_data = conditional_func(
Expand Down
2 changes: 1 addition & 1 deletion conflux_web3/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1601,7 +1601,7 @@ def get_logs(self, filter_params: FilterParams) -> List[LogReceipt]:...
@overload
def get_logs(self, filter_params: None=None, **kwargs: Any) -> List[LogReceipt]:...

def get_logs(self, filter_params: Optional[FilterParams]=None, **kwargs: Any) -> List[LogReceipt]:
def get_logs(self, filter_params: Optional[FilterParams]=None, **kwargs: Optional[FilterParams]) -> List[LogReceipt]:
"""
Returns logs matching the filter provided.
It is accepted to pass filter_params as a dict or by direclty specifying field name (but cannot mix)
Expand Down
2 changes: 1 addition & 1 deletion conflux_web3/contract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from web3._utils.datatypes import (
PropertyCheckingFactory,
)
from web3.types import (
from eth_typing import (
ABI
)

Expand Down
Loading
Loading