├── .bumpversion.cfg ├── .github └── workflows │ ├── coverage.yml │ ├── local-test.yml │ └── testnet-test.yml ├── .gitignore ├── CHANGELOG.md ├── MANIFEST.in ├── Makefile ├── README.md ├── _web3_hook └── __init__.py ├── cns ├── __init__.py ├── cns.py ├── exceptions.py ├── py.typed └── utils.py ├── code_style.md ├── conflux_web3 ├── __init__.py ├── _utils │ ├── __init__.py │ ├── abi.py │ ├── cns.py │ ├── contracts.py │ ├── decorators.py │ ├── disabled_eth_apis.py │ ├── events.py │ ├── method_formatters.py │ ├── normalizers.py │ ├── rpc_abi.py │ └── transactions.py ├── client.py ├── contract │ ├── __init__.py │ ├── caller.py │ ├── constructor.py │ ├── event.py │ ├── function.py │ └── metadata │ │ ├── AdminControl.json │ │ ├── ConfluxContext.json │ │ ├── Create2Factory.json │ │ ├── CrossSpaceCall.json │ │ ├── ENS.json │ │ ├── ERC1820.json │ │ ├── ERC20.json │ │ ├── ETHRegistrarController.json │ │ ├── Faucet.json │ │ ├── NameWrapper.json │ │ ├── ParamsControl.json │ │ ├── PoSRegister.json │ │ ├── RESOLVER.json │ │ ├── SponsorWhitelistControl.json │ │ ├── Staking.json │ │ └── __init__.py ├── dev │ └── __init__.py ├── exceptions.py ├── main.py ├── method.py ├── middleware │ ├── __init__.py │ ├── base.py │ ├── names.py │ ├── pending.py │ └── wallet.py ├── providers │ ├── __init__.py │ └── rpc.py ├── py.typed ├── txpool.py ├── types │ ├── __init__.py │ └── transaction_hash.py └── utils │ ├── __init__.py │ └── address.py ├── docs ├── en │ ├── .readthedocs.yaml │ ├── CHANGELOG.md │ ├── README.md │ ├── _config.yml │ ├── _toc.yml │ ├── api.rst │ ├── cfx_account.rst │ ├── cfx_address.rst │ ├── cfx_address.utils.rst │ ├── conf.py │ ├── examples │ │ ├── .gitkeep │ │ ├── 01-quickstart.ipynb │ │ ├── 02-address_account_and_wallet.ipynb │ │ ├── 03-trace_transaction_status.ipynb │ │ ├── 04-query_data_via_RPC.ipynb │ │ ├── 05-interact_with_contracts_and_process_logs.ipynb │ │ ├── 10-send_raw_transaction.ipynb │ │ ├── 11-construct_transaction_from_scratch.ipynb │ │ └── 12-sign_data_other_than_transactions.ipynb │ ├── token_unit.rst │ ├── types.rst │ └── util_types.rst ├── examples │ └── base │ │ ├── 01-quickstart.base.ipynb │ │ ├── 02-address_account_and_wallet.base.ipynb │ │ ├── 03-trace_transaction_status.base.ipynb │ │ ├── 04-query_data_via_RPC.base.ipynb │ │ ├── 05-interact_with_contracts_and_process_logs.base.ipynb │ │ ├── 10-send_raw_transaction.base.ipynb │ │ ├── 11-construct_transaction_from_scratch.base.ipynb │ │ └── 12-sign_data_other_than_transactions.base.ipynb ├── logo.png ├── requirements-doc.txt └── zh-CN │ ├── .readthedocs.yaml │ ├── README.md │ ├── _config.yml │ ├── _toc.yml │ ├── conf.py │ └── examples │ ├── .gitkeep │ ├── 01-quickstart.ipynb │ ├── 02-address_account_and_wallet.ipynb │ ├── 03-trace_transaction_status.ipynb │ ├── 04-query_data_via_RPC.ipynb │ ├── 05-interact_with_contracts_and_process_logs.ipynb │ ├── 10-send_raw_transaction.ipynb │ ├── 11-construct_transaction_from_scratch.ipynb │ └── 12-sign_data_other_than_transactions.ipynb ├── mmg.yml ├── postBuild ├── pyproject.toml ├── pytest.ini ├── runtime.txt ├── setup.py └── tests ├── __init__.py ├── _test_helpers ├── ENV_SETTING.py ├── __init__.py ├── amb_metadata.json ├── node.py ├── testnet │ ├── conflux.toml │ ├── log.yaml │ ├── pos_config │ │ ├── genesis_file │ │ ├── initial_nodes.json │ │ └── pos_config.yaml │ └── throttling.toml └── type_check.py ├── base_features ├── test_api.py └── test_default_account.py ├── cns ├── conftest.py └── test_cns.py ├── conftest.py ├── middleware ├── test_pending.py └── test_wallet.py ├── providers └── test_http_provider_retry.py ├── rpcs ├── cfx │ ├── conftest.py │ ├── filter │ │ ├── test_block_filter.py │ │ ├── test_log_filter.py │ │ └── test_pending_tx_filter.py │ ├── test_cfx_account_query_rpcs.py │ ├── test_cfx_block_rpcs.py │ ├── test_cfx_nonce_rpcs.py │ └── test_cfx_rpcs.py ├── test_cfx_status_query_rpcs.py ├── test_disabled_rpcs.py ├── test_pending_rpcs.py └── test_txpool_rpcs.py ├── test_node.py ├── transaction ├── contract │ ├── test_embed.py │ ├── test_encoding.py │ ├── test_erc20.py │ └── test_others.py ├── test_base_tx.py └── test_tx_mungers.py ├── type_hints └── test_contract_type_hint.py └── utils ├── test_contract_utils.py ├── test_gas_utils.py ├── test_test_utils.py └── test_transaction_utils.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.4.4 3 | commit = True 4 | tag = True 5 | parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-(?P[^.]*)\.(?P\d+))? 6 | serialize = 7 | {major}.{minor}.{patch}-{stage}.{devnum} 8 | {major}.{minor}.{patch} 9 | 10 | [bumpversion:part:stage] 11 | optional_value = stable 12 | first_value = stable 13 | values = 14 | alpha 15 | beta 16 | stable 17 | 18 | [bumpversion:part:devnum] 19 | 20 | [bumpversion:file:setup.py] 21 | search = VERSION = "{current_version}" 22 | replace = VERSION = "{new_version}" 23 | 24 | [bumpversion:file:tests/base_features/test_api.py] 25 | search = "{current_version}" 26 | replace = "{new_version}" 27 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: 4 | push: 5 | branches: ["v1"] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | python-version: ["3.9"] 14 | name: Test python API 15 | steps: 16 | - uses: actions/checkout@v1 17 | - name: Install requirements 18 | run: pip install ".[dev]" 19 | 20 | - name: Build package 21 | run: | 22 | make build 23 | 24 | - name: Install built package and avoid local import 25 | run: | 26 | pip install dist/*.whl --force-reinstall 27 | rm -rf conflux_web3 28 | - name: Run tests and collect coverage 29 | run: 30 | export TESTNET_URL=${{secrets.TESTNET_URL}} && 31 | export USE_TESTNET=1 && 32 | export ENS_ACCOUNT_NAME=${{secrets.ENS_ACCOUNT_NAME}} && 33 | export ENS_ACCOUNT_SECRET=${{secrets.ENS_ACCOUNT_SECRET}} && 34 | pytest --cov tests -n 3 --dist loadscope 35 | - name: Upload coverage reports to Codecov 36 | run: | 37 | curl -Os https://uploader.codecov.io/latest/linux/codecov 38 | chmod +x codecov 39 | ./codecov -t ${{secrets.CODECOV_TOKEN}} 40 | -------------------------------------------------------------------------------- /.github/workflows/local-test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | pull_request: 8 | branches: [ "dev", "v1"] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v3 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install dependencies 27 | run: | 28 | pip install ".[dev]" 29 | 30 | - name: Build package 31 | run: | 32 | make build 33 | 34 | - name: Install built package and avoid local import 35 | run: | 36 | pip install dist/*.whl --force-reinstall 37 | rm -rf conflux_web3 38 | 39 | - name: Run test 40 | run: | 41 | make test 42 | -------------------------------------------------------------------------------- /.github/workflows/testnet-test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | pull_request: 8 | branches: [ "dev", "v1"] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | python-version: ["3.13"] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v3 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install dependencies 27 | run: | 28 | pip install ".[dev]" 29 | 30 | - name: Build package 31 | run: | 32 | make build 33 | 34 | - name: Install built package and avoid local import 35 | run: | 36 | pip install dist/*.whl --force-reinstall 37 | rm -rf conflux_web3 38 | 39 | - name: Run test 40 | run: | 41 | export TESTNET_URL=${{secrets.TESTNET_URL}} && 42 | export USE_TESTNET=1 && 43 | export ENS_ACCOUNT_NAME=${{secrets.ENS_ACCOUNT_NAME}} && 44 | export ENS_ACCOUNT_SECRET=${{secrets.ENS_ACCOUNT_SECRET}} && 45 | export TEST_FINALIZATION=1 && 46 | make test 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .pytest_cache 3 | .hypothesis 4 | .env 5 | build 6 | dist 7 | tests/__pycache__ 8 | venv*/ 9 | conflux/__init__.pyc 10 | conflux.egg-info 11 | conflux_web3.egg-info 12 | bin/ 13 | lib/ 14 | include/ 15 | __pycache__ 16 | .DS_Store 17 | .vscode 18 | private_tests 19 | legacy_tests 20 | note*.ipynb 21 | share 22 | main*.py 23 | *.bk.py 24 | docs/*/_build 25 | docs/*/_autosummary 26 | .coverage 27 | docs/examples/zh 28 | docs/examples/en 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | docs/en/CHANGELOG.md -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include conflux_web3/contract/metadata *.json -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all build push 2 | 3 | all: build 4 | 5 | clean: 6 | rm -rf dist/ 7 | 8 | build: clean 9 | python3 setup.py sdist bdist_wheel 10 | 11 | publish: 12 | twine upload dist/* --repository conflux-web3 13 | 14 | rm-doc: 15 | rm -rf docs/en/_build 16 | rm -rf docs/zh-CN/_build 17 | gen-doc-config: 18 | jupyter-book config sphinx docs/en 19 | jupyter-book config sphinx docs/zh-CN 20 | 21 | gen-example-from-base: 22 | mmg -b mmg.yml 23 | cp -r docs/examples/en/* docs/en/examples/ 24 | cp -r docs/examples/zh/* docs/zh-CN/examples/ 25 | 26 | # Note the first line relies on jupyterbook==0.11.3 27 | doc: rm-doc gen-doc-config gen-example-from-base 28 | jupyter-book build docs/en/ 29 | jupyter-book build docs/zh-CN/ 30 | 31 | # gen-docs: 32 | # cd ./docs && \ 33 | # sphinx-apidoc -o ./source ../cfx_address -f -M --separate && \ 34 | # make html 35 | 36 | test: 37 | pytest tests -vv -n 3 --dist loadscope 38 | # && export USE_TESTNET=1 && pytest tests 39 | # cd ./docs && make doctest 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | docs/en/README.md -------------------------------------------------------------------------------- /_web3_hook/__init__.py: -------------------------------------------------------------------------------- 1 | # WARNING: 2 | # note that the hook won't effect already imported objects, 3 | # so make sure this module is executed before any module of conflux_web3 or web3 is executed 4 | # or the hook might not take effect 5 | # else try directly importing the package to hook 6 | 7 | from typing import ( 8 | TYPE_CHECKING, 9 | Callable, 10 | Dict, 11 | Union, 12 | ) 13 | from cfx_utils.post_import_hook import ( 14 | when_imported 15 | ) 16 | from cfx_address import ( 17 | Base32Address 18 | ) 19 | from cfx_utils.exceptions import ( 20 | InvalidEpochNumebrParam 21 | ) 22 | 23 | if TYPE_CHECKING: 24 | from conflux_web3 import ( 25 | Web3 26 | ) 27 | from conflux_web3.types import ( 28 | EpochNumberParam, 29 | ) 30 | 31 | 32 | 33 | def conditional_func(target_func: Callable, condition: Callable[..., bool]) -> Callable: 34 | """decorate a function to optionally execute another one 35 | if condition: 36 | target_func 37 | else: 38 | original_func 39 | 40 | Args: 41 | target_func (Callable): function to be executed if condition 42 | condition (Callable[..., bool]): receives func arguments, returns a bool 43 | """ 44 | def inner(func) : 45 | def wrapper(*args, **kwargs): 46 | if condition(*args, **kwargs): 47 | return target_func(*args, **kwargs) 48 | return func(*args, **kwargs) 49 | return wrapper 50 | return inner 51 | 52 | def conditional_post_func(post_func: Callable, condition: Callable[..., bool]) -> Callable: 53 | """decorate a function to optionally execute post operation 54 | rtn = original_func 55 | if condition: 56 | post_func 57 | return rtn 58 | 59 | Args: 60 | post_func (Callable): function to be executed after original function if condition 61 | condition (Callable[..., bool]): receives func arguments, returns a bool 62 | """ 63 | def inner(func) : 64 | def wrapper(*args, **kwargs): 65 | rtn = func(*args, **kwargs) 66 | if condition(*args, **kwargs): 67 | post_func(*args, **kwargs) 68 | else: 69 | pass 70 | return rtn 71 | return wrapper 72 | return inner 73 | 74 | def cfx_web3_condition(*args, **kwargs) -> bool: 75 | """ 76 | 77 | Returns: 78 | bool: returns if conflux_web3.Web3 type variable in arguments 79 | """ 80 | from conflux_web3 import Web3 81 | for arg in list(args)+list(kwargs.values()): 82 | if isinstance(arg, Web3): 83 | return True 84 | return False 85 | 86 | @when_imported("web3._utils.contracts") 87 | def hook_encode_abi(mod): 88 | if mod.__name__ == "web3._utils.contracts": 89 | from conflux_web3._utils.contracts import cfx_encode_abi 90 | mod.encode_abi = conditional_func( 91 | cfx_encode_abi, 92 | cfx_web3_condition 93 | )(mod.encode_abi) 94 | 95 | eth_to_cfx_tag_mapping: Dict[str, "EpochNumberParam"] = { 96 | "pending": "pending", 97 | "latest": "latest_state", 98 | "safe": "latest_confirmed", 99 | "finalized": "latest_finalized", 100 | } 101 | 102 | # used to hook web3.contract.parse_block_identifier 103 | # hook is activated in _web3_hook 104 | def cfx_parse_block_identifier( 105 | w3: "Web3", block_identifier: Union["EpochNumberParam", None] 106 | ) -> "EpochNumberParam": 107 | if block_identifier is None: 108 | return w3.cfx.default_block 109 | if isinstance(block_identifier, int): 110 | return block_identifier 111 | elif block_identifier in ['earliest', 'latest_checkpoint', 'latest_finalized', 'latest_confirmed', 'latest_state', 'latest_mined']: # get_args("EpochLiteral") 112 | return block_identifier 113 | elif isinstance(block_identifier, bytes) or ( 114 | isinstance(block_identifier, str) and block_identifier.startswith("0x") 115 | ): 116 | # r = 117 | # assert r is not None 118 | return w3.cfx.get_block_by_hash(block_identifier)["epochNumber"] # type: ignore 119 | elif block_identifier in eth_to_cfx_tag_mapping: 120 | return eth_to_cfx_tag_mapping[block_identifier] 121 | else: 122 | raise InvalidEpochNumebrParam 123 | 124 | @when_imported("web3._utils.contracts") 125 | def hook_parse_block_identifier(mod): 126 | # from conflux_web3.contract import cfx_parse_block_identifier 127 | if mod.__name__ == "web3._utils.contracts": 128 | mod.parse_block_identifier = conditional_func( 129 | cfx_parse_block_identifier, 130 | cfx_web3_condition 131 | )(mod.parse_block_identifier) 132 | 133 | 134 | # hooked to is_none_or_zero_address in _web3_hook 135 | def is_none_or_base_32_zero_address(addr) -> bool: 136 | EMPTY_ADDR_HEX = "0x" + "00" * 20 137 | try: 138 | return (not addr) or (addr == EMPTY_ADDR_HEX) or (Base32Address.decode(addr)["hex_address"] == EMPTY_ADDR_HEX) 139 | except: 140 | return False 141 | 142 | @when_imported("ens.utils") 143 | def hook_is_none_or_zero_address(mod): 144 | if mod.__name__ == "ens.utils": 145 | mod.is_none_or_zero_address = is_none_or_base_32_zero_address 146 | 147 | # we manually import ens.utils to hook is_none_or_zero_address because of insuccessful hook 148 | import ens.utils 149 | -------------------------------------------------------------------------------- /cns/__init__.py: -------------------------------------------------------------------------------- 1 | # should be placed at the most front 2 | import _web3_hook 3 | 4 | from cns.cns import ( 5 | CNS 6 | ) 7 | -------------------------------------------------------------------------------- /cns/exceptions.py: -------------------------------------------------------------------------------- 1 | class InterfaceNotSupported(Exception): 2 | pass 3 | 4 | class MissingTransactionSender(Exception): 5 | pass 6 | 7 | class UnstableAPI(Exception): 8 | pass 9 | -------------------------------------------------------------------------------- /cns/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/cns/py.typed -------------------------------------------------------------------------------- /cns/utils.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, 3 | Optional, 4 | Sequence, 5 | Tuple, 6 | cast, 7 | Collection, 8 | ) 9 | 10 | from ens.utils import ( 11 | default, 12 | ) 13 | from ens.constants import ( 14 | ACCEPTABLE_STALE_HOURS, 15 | ) 16 | 17 | from web3.exceptions import ( 18 | Web3ValueError, 19 | ) 20 | from web3.middleware.stalecheck import StalecheckMiddlewareBuilder 21 | from cfx_address import ( 22 | Base32Address 23 | ) 24 | 25 | if TYPE_CHECKING: 26 | from conflux_web3 import Web3 as _Web3 # noqa: F401 27 | from web3.providers.base import ( # noqa: F401 28 | # AsyncBaseProvider, 29 | BaseProvider, 30 | ) 31 | from conflux_web3.types import ( 32 | Middleware 33 | ) 34 | 35 | def init_web3( 36 | provider: "BaseProvider" = cast("BaseProvider", default), 37 | middlewares: Optional[Sequence[Tuple["Middleware", str]]] = None, 38 | default_account: Optional[Base32Address] = None 39 | ) -> "_Web3": 40 | from conflux_web3 import Web3 as Web3Main 41 | from conflux_web3.client import ConfluxClient as CfxMain 42 | 43 | if provider is default: 44 | w3 = Web3Main(ens=None, modules={"cfx": (CfxMain)}) # type: ignore 45 | else: 46 | w3 = Web3Main(provider, middlewares, ens=None, modules={"cfx": (CfxMain)}) # type: ignore 47 | if default_account: 48 | w3.cfx._default_account = default_account 49 | return customize_web3(w3) 50 | 51 | def build_stalecheck_middleware(allowable_delay: int, skip_stalecheck_for_methods: Collection[str]): 52 | def inner(w3): 53 | if allowable_delay <= 0: 54 | raise Web3ValueError( 55 | "You must set a positive allowable_delay in seconds for this middleware" 56 | ) 57 | middleware = StalecheckMiddlewareBuilder(w3) 58 | middleware.allowable_delay = allowable_delay 59 | middleware.skip_stalecheck_for_methods = skip_stalecheck_for_methods 60 | middleware.cache = {"latest": None} 61 | return middleware 62 | return inner 63 | 64 | 65 | def customize_web3(w3: "_Web3") -> "_Web3": 66 | if w3.middleware_onion.get("name_to_address"): 67 | w3.middleware_onion.remove("name_to_address") 68 | 69 | if not w3.middleware_onion.get("stalecheck"): 70 | w3.middleware_onion.add( 71 | build_stalecheck_middleware(ACCEPTABLE_STALE_HOURS * 3600, ["cfx_getBlockByEpochNumber", "cfx_getStatus"]), name="stalecheck" 72 | ) 73 | return w3 74 | 75 | # is_none_or_base_32_zero_address is implemented in _web3_hook 76 | # def is_none_or_base_32_zero_address(addr) -> bool: 77 | # ... -------------------------------------------------------------------------------- /code_style.md: -------------------------------------------------------------------------------- 1 | ## Code styles 2 | 3 | ### type hints 4 | 5 | - for inputs, be flexible 6 | - for outputs, be strict 7 | 8 | ```python 9 | # recommended 10 | def receives_address(address: Union[Base32Address, str]) -> Base32Address: 11 | return address 12 | 13 | def receives_hash(hash: Union[_Hash32, str]) -> HexBytes: 14 | return hash 15 | ``` 16 | 17 | such style suit for `conflux_web3.types, tests._test_helpers.type_check` 18 | 19 | Optional or | None 20 | - for inputs, use Optional 21 | - for outputs, use | None 22 | -------------------------------------------------------------------------------- /conflux_web3/__init__.py: -------------------------------------------------------------------------------- 1 | # should be placed at the most front 2 | import _web3_hook 3 | 4 | from importlib.metadata import version 5 | 6 | from conflux_web3.providers.rpc import HTTPProvider 7 | from conflux_web3.main import Web3 8 | from conflux_web3.dev import ( 9 | get_local_web3, 10 | get_mainnet_web3, 11 | get_testnet_web3 12 | ) 13 | 14 | __version__ = version("conflux_web3") 15 | 16 | __all__ = [ 17 | "Web3", 18 | "HTTPProvider", 19 | "get_local_web3", 20 | "get_mainnet_web3", 21 | "get_testnet_web3", 22 | ] 23 | -------------------------------------------------------------------------------- /conflux_web3/_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/conflux_web3/_utils/__init__.py -------------------------------------------------------------------------------- /conflux_web3/_utils/abi.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from eth_abi.registry import ( 3 | ABIRegistry, 4 | BaseEquals 5 | ) 6 | from eth_abi.decoding import ( 7 | AddressDecoder 8 | ) 9 | from eth_abi.exceptions import ( 10 | EncodingError 11 | ) 12 | from web3._utils.abi import ( 13 | build_non_strict_registry, 14 | AddressEncoder, 15 | ) 16 | from cfx_address.utils import ( 17 | normalize_to 18 | ) 19 | from cfx_utils.exceptions import ( 20 | InvalidAddress, 21 | ) 22 | from conflux_web3._utils.cns import ( 23 | is_cns_name 24 | ) 25 | 26 | class Base32AddressEncoder(AddressEncoder): 27 | 28 | encode_fn = lambda self, address: AddressEncoder.encode_fn(normalize_to(address, None)) 29 | 30 | @classmethod 31 | def validate_value(cls, value: Any) -> None: 32 | if is_cns_name(value): 33 | return 34 | try: 35 | normalize_to(value, None) 36 | except InvalidAddress: 37 | raise EncodingError(f"Not a valid Base32 address nor hex address: {value}") 38 | 39 | 40 | class CfxAddressDecoder(AddressDecoder): 41 | decode_fn = lambda x: x 42 | 43 | def build_cfx_default_registry() -> ABIRegistry: 44 | registry = build_non_strict_registry() 45 | 46 | registry.unregister('address') 47 | registry.register( 48 | BaseEquals('address'), 49 | Base32AddressEncoder, CfxAddressDecoder, 50 | label='address', 51 | ) 52 | 53 | return registry 54 | -------------------------------------------------------------------------------- /conflux_web3/_utils/cns.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, Any, Union 3 | ) 4 | from web3._utils.ens import ( 5 | is_ens_name 6 | ) 7 | from web3._utils.empty import ( 8 | empty 9 | ) 10 | from web3.exceptions import ( 11 | NameNotFound 12 | ) 13 | from cfx_address.utils import ( 14 | is_valid_base32 15 | ) 16 | from cfx_address import ( 17 | Base32Address 18 | ) 19 | from conflux_web3.exceptions import ( 20 | NameServiceNotSet 21 | ) 22 | 23 | if TYPE_CHECKING: 24 | from conflux_web3 import Web3 25 | 26 | def is_cns_name(value: Any) -> bool: 27 | if is_valid_base32(value): 28 | return False 29 | else: 30 | return is_ens_name(value) and "." in value 31 | 32 | def validate_cns_existence(w3: "Web3"): 33 | if w3.cns is empty: 34 | raise NameServiceNotSet("Web3's name service is not set") 35 | 36 | def resolve_if_cns_name(w3: "Web3", cns_name: str) -> Union[str, Base32Address]: 37 | if is_cns_name(cns_name): 38 | validate_cns_existence(w3) 39 | addr = w3.cns.address(cns_name) 40 | if addr: 41 | return addr 42 | raise NameNotFound(f"{cns_name} resolve failed") 43 | return cns_name 44 | -------------------------------------------------------------------------------- /conflux_web3/_utils/contracts.py: -------------------------------------------------------------------------------- 1 | # import functools 2 | from typing import ( 3 | TYPE_CHECKING, 4 | Any, 5 | Optional, 6 | Sequence, 7 | ) 8 | import json 9 | from hexbytes import HexBytes 10 | 11 | from eth_typing.encoding import HexStr 12 | from eth_utils.hexadecimal import encode_hex 13 | from eth_utils.conversions import to_hex 14 | 15 | from eth_typing import ABIFunction 16 | from eth_utils.abi import ( 17 | get_abi_input_types, 18 | ) 19 | from web3._utils import contracts 20 | from web3.utils.abi import ( 21 | check_if_arguments_can_be_encoded, 22 | ) 23 | from web3._utils.abi import ( 24 | map_abi_data, 25 | ) 26 | from web3._utils.normalizers import ( 27 | abi_bytes_to_bytes, 28 | abi_string_to_text, 29 | ) 30 | 31 | from conflux_web3._utils.normalizers import ( 32 | abi_cns_resolver 33 | ) 34 | 35 | if TYPE_CHECKING: 36 | from conflux_web3 import Web3 37 | 38 | # this api is used to hook web3._utils.contracts.encode_abi 39 | # hook is activated in _web3_hook 40 | def cfx_encode_abi( 41 | web3: "Web3", abi: ABIFunction, arguments: Sequence[Any], data: Optional[HexStr] = None 42 | ) -> HexStr: 43 | """ 44 | do what encode_abi does except for normalizers 45 | """ 46 | argument_types = get_abi_input_types(abi) 47 | 48 | if not check_if_arguments_can_be_encoded(abi, *arguments, abi_codec=web3.codec): 49 | raise TypeError( 50 | "One or more arguments could not be encoded to the necessary " 51 | "ABI type. Expected types are: {0}".format( 52 | ', '.join(argument_types), 53 | ) 54 | ) 55 | 56 | # abi_ens_resolver and abi_address_to_hex are eliminated 57 | normalizers = [ 58 | abi_cns_resolver(web3), # type: ignore 59 | # abi_address_to_hex, 60 | abi_bytes_to_bytes, 61 | abi_string_to_text, 62 | ] 63 | normalized_arguments = map_abi_data( 64 | normalizers, 65 | argument_types, 66 | arguments, 67 | ) 68 | encoded_arguments = web3.codec.encode( 69 | argument_types, 70 | normalized_arguments, 71 | ) 72 | 73 | if data: 74 | return to_hex(HexBytes(data) + encoded_arguments) 75 | else: 76 | return encode_hex(encoded_arguments) 77 | 78 | prepare_transaction = contracts.prepare_transaction 79 | -------------------------------------------------------------------------------- /conflux_web3/_utils/decorators.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import sys 3 | from typing import ( 4 | Any, 5 | Callable, 6 | NoReturn, 7 | Optional, 8 | TypeVar, 9 | Union, 10 | overload, 11 | ) 12 | from typing_extensions import ( 13 | ParamSpec, 14 | ) 15 | 16 | from conflux_web3.exceptions import ( 17 | DisabledException 18 | ) 19 | 20 | if sys.version_info < (3, 8): 21 | from cached_property import cached_property 22 | else: 23 | from functools import cached_property 24 | 25 | T = TypeVar("T") 26 | P = ParamSpec("P") 27 | 28 | 29 | def temp_alter_module_variable(module: Any, varname: str, new_var: Any, condition: Callable[..., bool]): 30 | """temporarily alters module.varname to new_var before func is called, and recover the change afterwards 31 | 32 | Args: 33 | module (Any): the module to be temporarily changed 34 | varname (str): varname of the var to be changed 35 | new_var (Any): _description_ 36 | condition (Callable[..., Boolean]): _description_ 37 | """ 38 | def inner(func: Callable[P, T]) -> Callable[P, T]: 39 | def wrapper(*args: P.args, **kwargs: P.kwargs): 40 | if condition(*args, **kwargs): 41 | cache = getattr(module, varname) 42 | module.__setattr__(varname, new_var) 43 | result = func(*args, **kwargs) 44 | module.__setattr__(varname, cache) 45 | return result 46 | return func(*args, **kwargs) 47 | return wrapper 48 | return inner 49 | 50 | @overload 51 | def use_instead(func: None=None, *, origin: str="This web3.eth api", substitute: Optional[str]=None) -> Callable[..., Callable[..., NoReturn]]: ... 52 | 53 | @overload 54 | def use_instead(func: Callable[..., Any], *, origin: str="This web3.eth api", substitute: Optional[str]=None) -> Callable[..., NoReturn]: ... 55 | 56 | def use_instead( 57 | func: Optional[Callable[..., Any]]=None, *, origin: str="This web3.eth api", substitute: Optional[str]=None 58 | ) -> Union[Callable[..., Callable[..., NoReturn]], Callable[..., NoReturn]]: 59 | if func is None: 60 | return functools.partial(use_instead, origin=origin, substitute=substitute) # type: ignore 61 | 62 | @functools.wraps(func) 63 | def inner(*args: Any, **kwargs: Any) -> NoReturn: 64 | if substitute is None: 65 | raise DisabledException(f"{origin} is not valid in Conflux Network." 66 | "Check https://developer.confluxnetwork.org/conflux-doc/docs/json_rpc/#migrating-from-ethereum-json-rpc for more information") 67 | else: 68 | raise DisabledException(f"{origin} is not valid in Conflux Network, " 69 | f"use {substitute} instead") 70 | return inner 71 | 72 | __all__ = [ 73 | "cached_property", 74 | "use_instead", 75 | "temp_alter_module_variable", 76 | ] 77 | -------------------------------------------------------------------------------- /conflux_web3/_utils/disabled_eth_apis.py: -------------------------------------------------------------------------------- 1 | from conflux_web3.exceptions import DisabledException 2 | 3 | disabled_method_list = [ 4 | "get_proof", 5 | # "get_block", 6 | "get_block_transaction_count", 7 | "get_uncle_count", 8 | "get_uncle_by_block", 9 | "get_raw_transaction", 10 | "get_raw_transaction_by_block", 11 | "get_transaction_by_block", 12 | # "get_transaction_count", 13 | # "replace_transaction", 14 | # "modify_transaction" 15 | # "estimate_gas", 16 | "submit_hashrate", 17 | "submit_work", 18 | ] 19 | 20 | disabled_property_list = [ 21 | "syncing", 22 | "coinbase", 23 | "mining", 24 | "hashrate", 25 | "block_number", 26 | "get_work", 27 | ] 28 | 29 | disabled_list = disabled_method_list + disabled_property_list 30 | -------------------------------------------------------------------------------- /conflux_web3/_utils/events.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | Optional, 3 | cast, 4 | Any, 5 | ) 6 | import itertools 7 | 8 | from eth_utils.abi import ( 9 | get_abi_input_names, 10 | ) 11 | from eth_abi.codec import ( 12 | ABICodec, 13 | ) 14 | 15 | from eth_utils.conversions import ( 16 | to_bytes, 17 | ) 18 | from eth_utils.toolz import ( 19 | curry, # type: ignore 20 | ) 21 | 22 | from web3._utils.events import ( 23 | get_event_abi_types_for_decoding, 24 | ) 25 | from web3._utils.abi import ( 26 | exclude_indexed_event_inputs, 27 | get_indexed_event_inputs, 28 | map_abi_data, 29 | normalize_event_input_types, 30 | named_tree 31 | ) 32 | from web3.utils.abi import ( 33 | get_event_log_topics, 34 | ) 35 | from eth_typing import ( 36 | ABIEvent, 37 | ) 38 | from web3.datastructures import ( 39 | AttributeDict 40 | ) 41 | from web3.exceptions import ( 42 | InvalidEventABI, 43 | LogTopicError, 44 | ) 45 | from web3._utils.encoding import ( 46 | hexstr_if_str, 47 | ) 48 | 49 | from cfx_address.utils import ( 50 | normalize_to 51 | ) 52 | from conflux_web3.types import ( 53 | EventData, 54 | LogReceipt, 55 | ) 56 | 57 | def _log_entry_data_to_bytes( 58 | log_entry_data: Any, 59 | ): 60 | return hexstr_if_str(to_bytes, log_entry_data) # type: ignore 61 | 62 | def get_cfx_base32_normalizer(chain_id: int): # type: ignore 63 | 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 64 | 65 | 66 | @curry # type: ignore 67 | def cfx_get_event_data( 68 | abi_codec: ABICodec, 69 | event_abi: ABIEvent, 70 | log_entry: LogReceipt, 71 | chain_id: Optional[int]= None 72 | ) -> EventData: 73 | """ 74 | Given an event ABI and a log entry for that event, return the decoded 75 | event data 76 | """ 77 | log_topics = get_event_log_topics(event_abi, log_entry["topics"]) 78 | log_topics_bytes = [_log_entry_data_to_bytes(topic) for topic in log_topics] 79 | log_topics_abi = get_indexed_event_inputs(event_abi) 80 | log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) 81 | log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs) 82 | log_topic_names = get_abi_input_names( 83 | ABIEvent({"name": event_abi["name"], "type": "event", "inputs": log_topics_abi}) 84 | ) 85 | 86 | if len(log_topics_bytes) != len(log_topic_types): 87 | raise LogTopicError( 88 | f"Expected {len(log_topic_types)} log topics. Got {len(log_topics_bytes)}" 89 | ) 90 | 91 | log_data = _log_entry_data_to_bytes(log_entry["data"]) 92 | log_data_abi = exclude_indexed_event_inputs(event_abi) 93 | log_data_normalized_inputs = normalize_event_input_types(log_data_abi) 94 | log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs) 95 | log_data_names = get_abi_input_names( 96 | ABIEvent({"name": event_abi["name"], "type": "event", "inputs": log_data_abi}) 97 | ) 98 | 99 | # sanity check that there are not name intersections between the topic 100 | # names and the data argument names. 101 | duplicate_names = set(log_topic_names).intersection(log_data_names) 102 | if duplicate_names: 103 | raise InvalidEventABI( 104 | "The following argument names are duplicated " 105 | f"between event inputs: '{', '.join(duplicate_names)}'" 106 | ) 107 | 108 | decoded_log_data = abi_codec.decode(log_data_types, log_data) 109 | normalized_log_data = map_abi_data( 110 | [get_cfx_base32_normalizer(chain_id)], log_data_types, decoded_log_data 111 | ) 112 | named_log_data = named_tree( 113 | log_data_normalized_inputs, 114 | normalized_log_data, 115 | ) 116 | 117 | decoded_topic_data = [ 118 | abi_codec.decode([topic_type], topic_data)[0] 119 | for topic_type, topic_data in zip(log_topic_types, log_topics_bytes) 120 | ] 121 | normalized_topic_data = map_abi_data( 122 | [get_cfx_base32_normalizer(chain_id)], log_topic_types, decoded_topic_data 123 | ) 124 | 125 | event_args = dict( 126 | itertools.chain( 127 | zip(log_topic_names, normalized_topic_data), 128 | named_log_data.items(), 129 | ) 130 | ) 131 | 132 | event_data = { 133 | "args": event_args, 134 | "event": event_abi.get("name", None), 135 | "logIndex": log_entry.get("logIndex", None), 136 | "transactionIndex": log_entry.get("transactionIndex", None), 137 | "transactionLogIndex": log_entry.get("transactionLogIndex", None), 138 | "transactionHash": log_entry.get("transactionHash", None), 139 | "address": log_entry["address"], 140 | "blockHash": log_entry.get("blockHash", None), 141 | "epochNumber": log_entry.get("epochNumber", None) 142 | } 143 | if isinstance(log_entry, AttributeDict): 144 | return cast(EventData, AttributeDict.recursive(event_data)) 145 | 146 | return event_data 147 | 148 | 149 | # events.get_event_data = conditional_func( 150 | # cfx_get_event_data, 151 | # from_cfx_condition 152 | # )(events.get_event_data) 153 | 154 | 155 | # modify_to_conditional_func(events.get_event_data, cfx_get_event_data, from_cfx_condition) 156 | 157 | 158 | -------------------------------------------------------------------------------- /conflux_web3/_utils/normalizers.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, 3 | Any, 4 | Tuple, 5 | ) 6 | 7 | from eth_typing import ( 8 | TypeStr, 9 | ) 10 | 11 | from eth_utils.toolz import ( 12 | curry, # type: ignore 13 | ) 14 | 15 | from cfx_address import ( 16 | Base32Address 17 | ) 18 | from conflux_web3._utils.cns import ( 19 | is_cns_name, 20 | resolve_if_cns_name, 21 | ) 22 | from conflux_web3.exceptions import ( 23 | NoWeb3Exception 24 | ) 25 | 26 | if TYPE_CHECKING: 27 | from conflux_web3 import Web3 # noqa: F401 28 | 29 | def rpc_snake_to_camel(snake_str: str) -> str: 30 | components = snake_str.split('_') 31 | rtn = "" 32 | for i, word in enumerate(components): 33 | rtn += word if i == 0 else word.title() 34 | return rtn 35 | 36 | @curry 37 | def abi_cns_resolver(w3: "Web3", type_str: TypeStr, val: Any) -> Tuple[TypeStr, Any]: 38 | if type_str == "address" and is_cns_name(val): 39 | if w3 is None: 40 | raise NoWeb3Exception( 41 | f"Could not look up name {val!r} because no web3" 42 | " connection available" 43 | ) 44 | return type_str, resolve_if_cns_name(w3, val) 45 | else: 46 | return type_str, val 47 | 48 | @curry 49 | def addresses_to_verbose_base32( 50 | network_id: int, type_str: TypeStr, data: Any 51 | ) -> Tuple[TypeStr, Base32Address]: 52 | if type_str == "address": 53 | return type_str, Base32Address(data, network_id, verbose=True, _ignore_invalid_type=True) 54 | return type_str, data 55 | -------------------------------------------------------------------------------- /conflux_web3/_utils/transactions.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import ( 3 | TYPE_CHECKING, 4 | List, 5 | Optional, 6 | cast, 7 | ) 8 | from hexbytes import HexBytes 9 | from eth_utils.toolz import ( 10 | # assoc, 11 | curry, # type: ignore 12 | merge, # type: ignore 13 | ) 14 | 15 | from cfx_utils.token_unit import ( 16 | Drip 17 | ) 18 | from cfx_utils.types import ( 19 | CIP1559TxDict 20 | ) 21 | from conflux_web3.exceptions import ( 22 | NoWeb3Exception 23 | ) 24 | from conflux_web3.types import ( 25 | TxParam, 26 | ) 27 | from conflux_web3._utils.cns import ( 28 | resolve_if_cns_name, 29 | ) 30 | 31 | if TYPE_CHECKING: 32 | from conflux_web3 import Web3 33 | 34 | 35 | LEGACY_TRANSACTION_DEFAULTS = { 36 | "value": 0, 37 | "data": b"", 38 | "nonce": lambda w3, tx, estimate=None: w3.cfx.get_next_nonce(tx['from']), # type: ignore 39 | "gas": lambda w3, tx, estimate=None: estimate["gasLimit"], # type: ignore 40 | "storageLimit": lambda w3, tx, estimate=None: estimate["storageCollateralized"], # type: ignore 41 | # convert to int value 42 | "gasPrice": lambda w3, tx, estimate=None: w3.cfx.gas_price.to(Drip).value, # type: ignore 43 | "chainId": lambda w3, tx, estimate=None: w3.cfx.chain_id, # type: ignore 44 | "epochHeight": lambda w3, tx, estimate=None: w3.cfx.epoch_number, # type: ignore 45 | } 46 | 47 | 48 | @curry 49 | def fill_transaction_defaults(w3: "Web3", transaction: TxParam) -> TxParam: 50 | """ 51 | Fill the necessary fields to "send" a transaction 52 | Before this function is invoked, ensure 'from' field is filled 53 | """ 54 | if not w3: 55 | raise NoWeb3Exception("A web3 object is required to fill transaction defaults, but no web3 object is passed") 56 | if (not transaction.get("from")) and (transaction.get("nonce", None) is None): 57 | raise ValueError("Transaction's 'from' field is required to fill nonce field") 58 | if "from" in transaction: 59 | transaction['from'] = resolve_if_cns_name(w3, transaction['from']) 60 | 61 | transaction_type = tell_transaction_type(transaction) 62 | if transaction_type == 0: 63 | return set_legacy_transaction_defaults(w3, transaction) 64 | elif transaction_type == 2: 65 | return set_cip1559_transaction_defaults(w3, transaction) 66 | else: 67 | raise ValueError(f"Transaction type not supported: {transaction_type}") 68 | 69 | def tell_transaction_type(transaction: TxParam) -> int: 70 | if "type" in transaction: 71 | return int(HexBytes(transaction["type"])) 72 | if "gasPrice" in transaction: 73 | return 0 74 | if "maxFeePerGas" in transaction or "maxPriorityFeePerGas" in transaction: 75 | return 2 76 | return 2 77 | 78 | def set_legacy_transaction_defaults(w3: "Web3", transaction: TxParam) -> TxParam: 79 | estimate = None 80 | if "value" not in transaction: 81 | transaction["value"] = 0 82 | if "data" not in transaction: 83 | transaction["data"] = b"" 84 | if "nonce" not in transaction: 85 | transaction["nonce"] = w3.cfx.get_next_nonce(transaction["from"]) 86 | if "gas" not in transaction: 87 | estimate = w3.cfx.estimate_gas_and_collateral(transaction) 88 | transaction["gas"] = estimate["gasLimit"] 89 | if "storageLimit" not in transaction: 90 | if not estimate: 91 | estimate = w3.cfx.estimate_gas_and_collateral(transaction) 92 | transaction["storageLimit"] = estimate["storageCollateralized"] 93 | if "gasPrice" not in transaction: 94 | transaction["gasPrice"] = w3.cfx.gas_price.to(Drip).value 95 | if "chainId" not in transaction: 96 | transaction["chainId"] = w3.cfx.chain_id 97 | if "epochHeight" not in transaction: 98 | transaction["epochHeight"] = w3.cfx.epoch_number 99 | transaction["type"] = 0 100 | return transaction 101 | 102 | def set_cip1559_transaction_defaults(w3: "Web3", transaction: CIP1559TxDict) -> TxParam: 103 | estimate = None 104 | if "value" not in transaction: 105 | transaction["value"] = 0 106 | if "data" not in transaction: 107 | transaction["data"] = b"" 108 | if "nonce" not in transaction: 109 | transaction["nonce"] = w3.cfx.get_next_nonce(transaction["from"]) 110 | if "gas" not in transaction: 111 | estimate = w3.cfx.estimate_gas_and_collateral(transaction) 112 | transaction["gas"] = estimate["gasLimit"] 113 | if "storageLimit" not in transaction: 114 | if not estimate: 115 | estimate = w3.cfx.estimate_gas_and_collateral(transaction) 116 | transaction["storageLimit"] = estimate["storageCollateralized"] 117 | if "maxPriorityFeePerGas" not in transaction: 118 | transaction["maxPriorityFeePerGas"] = w3.cfx.max_priority_fee.to(Drip).value 119 | if "maxFeePerGas" not in transaction: 120 | base_fee = w3.cfx.get_block("latest_state")["baseFeePerGas"] * 2 121 | transaction["maxFeePerGas"] = (base_fee + Drip(transaction["maxPriorityFeePerGas"])).to(Drip).value 122 | if "chainId" not in transaction: 123 | transaction["chainId"] = w3.cfx.chain_id 124 | if "epochHeight" not in transaction: 125 | transaction["epochHeight"] = w3.cfx.epoch_number 126 | transaction["type"] = 2 127 | return transaction -------------------------------------------------------------------------------- /conflux_web3/contract/caller.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, 3 | Optional, 4 | ) 5 | 6 | from eth_utils.toolz import ( 7 | partial, 8 | ) 9 | from eth_typing import ( 10 | ABI, 11 | ) 12 | 13 | from web3.contract.base_contract import ( 14 | BaseContractCaller, 15 | ) 16 | 17 | 18 | from conflux_web3.types import ( 19 | TxParam, 20 | AddressParam, 21 | EpochNumberParam, 22 | ) 23 | 24 | from .function import ( 25 | ConfluxContractFunctions, 26 | ) 27 | 28 | if TYPE_CHECKING: 29 | from conflux_web3 import Web3 30 | 31 | 32 | class ConfluxContractCaller(BaseContractCaller): 33 | def __init__( 34 | self, 35 | abi: ABI, 36 | w3: "Web3", 37 | address: AddressParam, 38 | transaction: Optional[TxParam] = None, 39 | block_identifier: EpochNumberParam = "latest_state", 40 | ccip_read_enabled: Optional[bool] = None, 41 | decode_tuples: Optional[bool] = False, 42 | contract_functions: Optional[ConfluxContractFunctions] = None, 43 | ) -> None: 44 | super().__init__( 45 | abi, 46 | w3, 47 | address, # type: ignore 48 | decode_tuples=decode_tuples 49 | ) 50 | 51 | 52 | if self.abi: 53 | if transaction is None: 54 | transaction = {} 55 | 56 | if contract_functions is None: 57 | contract_functions = ConfluxContractFunctions( 58 | abi, w3, address=address, decode_tuples=decode_tuples 59 | ) 60 | 61 | self._functions = contract_functions._functions 62 | for fn in contract_functions.__iter__(): 63 | caller_method = partial( 64 | self.call_function, 65 | fn, 66 | transaction=transaction, 67 | block_identifier=block_identifier, 68 | ccip_read_enabled=ccip_read_enabled, 69 | ) 70 | setattr(self, str(fn.abi_element_identifier), caller_method) 71 | 72 | 73 | def __call__( 74 | self, 75 | transaction: Optional[TxParam] = None, 76 | block_identifier: EpochNumberParam = "latest_state", 77 | ccip_read_enabled: Optional[bool] = None, 78 | ) -> "ConfluxContractCaller": 79 | if transaction is None: 80 | transaction = {} 81 | return type(self)( 82 | self.abi, 83 | self.w3, # type: ignore 84 | self.address, 85 | transaction=transaction, 86 | block_identifier=block_identifier, 87 | ccip_read_enabled=ccip_read_enabled, 88 | decode_tuples=self.decode_tuples, 89 | ) 90 | -------------------------------------------------------------------------------- /conflux_web3/contract/constructor.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, 3 | Optional 4 | ) 5 | from web3.contract import ( 6 | ContractConstructor 7 | ) 8 | 9 | from cfx_utils.decorators import ( 10 | combomethod 11 | ) 12 | from conflux_web3.types.transaction_hash import ( 13 | TransactionHash 14 | ) 15 | from conflux_web3.types import ( 16 | TxParam, 17 | EpochNumberParam, 18 | EstimateResult 19 | ) 20 | from conflux_web3._utils.transactions import ( 21 | fill_transaction_defaults 22 | ) 23 | 24 | if TYPE_CHECKING: 25 | from conflux_web3 import Web3 26 | 27 | class ConfluxContractConstructor(ContractConstructor): 28 | w3: "Web3" 29 | 30 | @combomethod 31 | def transact(self, transaction: Optional[TxParam] = None) -> TransactionHash: 32 | return super().transact(transaction) 33 | 34 | @combomethod 35 | def build_transaction(self, transaction: Optional[TxParam] = None) -> TxParam: 36 | built_transaction = self._build_transaction(transaction) 37 | return fill_transaction_defaults(self.w3, built_transaction) 38 | 39 | @combomethod 40 | def estimate_gas( 41 | self, 42 | transaction: Optional[TxParam] = None, 43 | block_identifier: Optional[EpochNumberParam] = None, 44 | ) -> EstimateResult: 45 | return self.estimate_gas_and_collateral(transaction, block_identifier) 46 | 47 | @combomethod 48 | def estimate_gas_and_collateral( 49 | self, 50 | transaction: Optional[TxParam] = None, 51 | block_identifier: Optional[EpochNumberParam] = None, 52 | ) -> EstimateResult: 53 | return super().estimate_gas(transaction, block_identifier) 54 | -------------------------------------------------------------------------------- /conflux_web3/contract/function.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, 3 | Any, 4 | Optional, 5 | ) 6 | 7 | from eth_typing import ( 8 | ABI, 9 | HexStr, 10 | ABICallable, 11 | ) 12 | from eth_utils.abi import ( 13 | abi_to_signature, 14 | ) 15 | 16 | from web3.contract.base_contract import ( 17 | BaseContractFunctions, 18 | ) 19 | from web3.contract.contract import ( 20 | ContractFunction, 21 | ) 22 | 23 | from web3.contract.utils import ( 24 | call_contract_function 25 | ) 26 | 27 | from web3.types import ( 28 | StateOverride, 29 | ABIElementIdentifier, 30 | ) 31 | 32 | from cfx_utils.decorators import ( 33 | combomethod, 34 | ) 35 | 36 | from cfx_utils.token_unit import ( 37 | to_int_if_drip_units 38 | ) 39 | from cfx_address import ( 40 | Base32Address 41 | ) 42 | from conflux_web3.types import ( 43 | TxParam, 44 | TxDict, 45 | AddressParam, 46 | EpochNumberParam, 47 | ) 48 | from conflux_web3._utils.contracts import ( 49 | prepare_transaction, 50 | ) 51 | from conflux_web3._utils.transactions import ( 52 | fill_transaction_defaults, 53 | ) 54 | from conflux_web3._utils.normalizers import ( 55 | addresses_to_verbose_base32 56 | ) 57 | 58 | if TYPE_CHECKING: 59 | from conflux_web3 import Web3 60 | from conflux_web3.types.transaction_hash import TransactionHash 61 | 62 | def build_transaction_for_function( 63 | address: Base32Address, 64 | web3: 'Web3', 65 | function_name: Optional[ABIElementIdentifier] = None, 66 | transaction: Optional[TxParam] = None, 67 | contract_abi: Optional[ABI] = None, 68 | abi_callable: Optional[ABICallable] = None, 69 | *args: Any, 70 | **kwargs: Any) -> TxParam: 71 | """Builds a dictionary with the fields required to make the given transaction 72 | 73 | Don't call this directly, instead use :meth:`Contract.build_transaction` 74 | on your contract instance. 75 | """ 76 | prepared_transaction:TxParam = prepare_transaction( 77 | address, # type: ignore 78 | web3, 79 | abi_element_identifier=function_name, # type: ignore 80 | contract_abi=contract_abi, 81 | abi_callable=abi_callable, 82 | transaction=transaction, # type: ignore 83 | fn_args=args, 84 | fn_kwargs=kwargs, 85 | ) 86 | 87 | prepared_transaction = fill_transaction_defaults(web3, prepared_transaction) 88 | 89 | return prepared_transaction # type: ignore 90 | 91 | class ConfluxContractFunction(ContractFunction): 92 | w3: "Web3" 93 | address: Base32Address 94 | 95 | def __call__(self, *args: Any, **kwargs: Any) -> "ConfluxContractFunction": 96 | return super().__call__(*args, **kwargs) # type: ignore 97 | 98 | def build_transaction(self, transaction: Optional[TxParam] = None) -> TxDict: 99 | built_transaction = self._build_transaction(transaction) # type: ignore 100 | abi_element_identifier = abi_to_signature(self.abi) 101 | return build_transaction_for_function( 102 | self.address, 103 | self.w3, 104 | abi_element_identifier, 105 | built_transaction, # type: ignore 106 | self.contract_abi, 107 | self.abi, 108 | *self.args, 109 | **self.kwargs, 110 | ) 111 | 112 | def call(self, 113 | transaction: Optional[TxParam] = None, 114 | block_identifier: Optional[EpochNumberParam] = "latest_state", 115 | state_override: Optional[StateOverride] = None, 116 | ccip_read_enabled: Optional[bool] = None) -> Any: 117 | call_transaction = self._get_call_txparams(transaction) # type: ignore 118 | 119 | # block_id = parse_block_identifier(self.w3, block_identifier) 120 | abi_element_identifier = abi_to_signature(self.abi) 121 | 122 | return call_contract_function( 123 | self.w3, 124 | self.address, # type: ignore 125 | # self._return_data_normalizers, 126 | [ 127 | addresses_to_verbose_base32(self.w3.cfx.chain_id), # type: ignore 128 | ], 129 | abi_element_identifier, 130 | call_transaction, 131 | block_identifier, # type: ignore 132 | self.contract_abi, 133 | self.abi, 134 | state_override, 135 | ccip_read_enabled, 136 | self.decode_tuples, 137 | *self.args, 138 | **self.kwargs, 139 | ) 140 | 141 | def transact(self, transaction: Optional[TxParam] = None) -> "TransactionHash": 142 | if transaction and "value" in transaction: 143 | transaction["value"] = to_int_if_drip_units(transaction["value"]) 144 | return super().transact(transaction) # type: ignore 145 | 146 | @combomethod 147 | def encode_transaction_data(cls) -> HexStr: 148 | return cls._encode_transaction_data() 149 | 150 | # @classmethod 151 | # def factory(cls, class_name: str, **kwargs: Any) -> "ConfluxContractFunction": 152 | # return cast(ConfluxContractFunction, PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi"))) 153 | 154 | 155 | class ConfluxContractFunctions(BaseContractFunctions[ConfluxContractFunction]): 156 | def __init__( 157 | self, 158 | abi: ABI, 159 | w3: "Web3", 160 | address: Optional[AddressParam] = None, 161 | decode_tuples: Optional[bool] = False, 162 | ) -> None: 163 | super().__init__( 164 | abi, 165 | w3, 166 | ConfluxContractFunction, 167 | address, # type: ignore 168 | decode_tuples 169 | ) 170 | -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/AdminControl.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "destroy", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "getAdmin", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" }, { "internalType": "address", "name": "newAdmin", "type": "address" } ], "name": "setAdmin", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/ConfluxContext.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "inputs": [], "name": "epochNumber", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "posHeight", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "finalizedEpochNumber", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "name": "epochHash", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "stateMutability": "view", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/Create2Factory.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "inputs": [ { "internalType": "address", "name": "addr", "type": "address" } ], "name": "isDeployed", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "bytes", "name": "code", "type": "bytes" }, { "internalType": "uint256", "name": "salt", "type": "uint256" } ], "name": "deploy", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "nonpayable", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/CrossSpaceCall.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "bytes20", "name": "sender", "type": "bytes20" }, { "indexed": true, "internalType": "bytes20", "name": "receiver", "type": "bytes20" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "indexed": false, "internalType": "bytes", "name": "data", "type": "bytes" } ], "name": "Call", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "bytes20", "name": "sender", "type": "bytes20" }, { "indexed": true, "internalType": "bytes20", "name": "contract_address", "type": "bytes20" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "indexed": false, "internalType": "bytes", "name": "init", "type": "bytes" } ], "name": "Create", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "bool", "name": "success", "type": "bool" } ], "name": "Outcome", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "bytes20", "name": "sender", "type": "bytes20" }, { "indexed": true, "internalType": "address", "name": "receiver", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" } ], "name": "Withdraw", "type": "event" }, { "inputs": [ { "internalType": "bytes", "name": "init", "type": "bytes" } ], "name": "createEVM", "outputs": [ { "internalType": "bytes20", "name": "", "type": "bytes20" } ], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "internalType": "bytes20", "name": "to", "type": "bytes20" } ], "name": "transferEVM", "outputs": [ { "internalType": "bytes", "name": "output", "type": "bytes" } ], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "internalType": "bytes20", "name": "to", "type": "bytes20" }, { "internalType": "bytes", "name": "data", "type": "bytes" } ], "name": "callEVM", "outputs": [ { "internalType": "bytes", "name": "output", "type": "bytes" } ], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "internalType": "bytes20", "name": "to", "type": "bytes20" }, { "internalType": "bytes", "name": "data", "type": "bytes" } ], "name": "staticCallEVM", "outputs": [ { "internalType": "bytes", "name": "output", "type": "bytes" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "deployEip1820", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "withdrawFromMapped", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "addr", "type": "address" } ], "name": "mappedBalance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "addr", "type": "address" } ], "name": "mappedNonce", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/ENS.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "constant": true, "inputs": [{"name": "node", "type": "bytes32"}], "name": "resolver", "outputs": [{"name": "", "type": "address"}], "payable": false, "type": "function" }, { "constant": true, "inputs": [{"name": "node", "type": "bytes32"}], "name": "owner", "outputs": [{"name": "", "type": "address"}], "payable": false, "type": "function" }, { "constant": false, "inputs": [ {"name": "node", "type": "bytes32"}, {"name": "label", "type": "bytes32"}, {"name": "owner", "type": "address"} ], "name": "setSubnodeOwner", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [ {"name": "node", "type": "bytes32"}, {"name": "ttl", "type": "uint64"} ], "name": "setTTL", "outputs": [], "payable": false, "type": "function" }, { "constant": true, "inputs": [{"name": "node", "type": "bytes32"}], "name": "ttl", "outputs": [{"name": "", "type": "uint64"}], "payable": false, "type": "function" }, { "constant": false, "inputs": [ {"name": "node", "type": "bytes32"}, {"name": "resolver", "type": "address"} ], "name": "setResolver", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [ {"name": "node", "type": "bytes32"}, {"name": "owner", "type": "address"} ], "name": "setOwner", "outputs": [], "payable": false, "type": "function" }, { "anonymous": false, "inputs": [ {"indexed": true, "name": "node", "type": "bytes32"}, {"indexed": false, "name": "owner", "type": "address"} ], "name": "Transfer", "type": "event" }, { "anonymous": false, "inputs": [ {"indexed": true, "name": "node", "type": "bytes32"}, {"indexed": true, "name": "label", "type": "bytes32"}, {"indexed": false, "name": "owner", "type": "address"} ], "name": "NewOwner", "type": "event" }, { "anonymous": false, "inputs": [ {"indexed": true, "name": "node", "type": "bytes32"}, {"indexed": false, "name": "resolver", "type": "address"} ], "name": "NewResolver", "type": "event" }, { "anonymous": false, "inputs": [ {"indexed": true, "name": "node", "type": "bytes32"}, {"indexed": false, "name": "ttl", "type": "uint64"} ], "name": "NewTTL", "type": "event" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/ERC1820.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "constant": false, "inputs": [ { "name": "_addr", "type": "address" }, { "name": "_interfaceHash", "type": "bytes32" }, { "name": "_implementer", "type": "address" } ], "name": "setInterfaceImplementer", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_addr", "type": "address" } ], "name": "getManager", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_addr", "type": "address" }, { "name": "_newManager", "type": "address" } ], "name": "setManager", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_interfaceName", "type": "string" } ], "name": "interfaceHash", "outputs": [ { "name": "", "type": "bytes32" } ], "payable": false, "stateMutability": "pure", "type": "function" }, { "constant": false, "inputs": [ { "name": "_contract", "type": "address" }, { "name": "_interfaceId", "type": "bytes4" } ], "name": "updateERC165Cache", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_addr", "type": "address" }, { "name": "_interfaceHash", "type": "bytes32" } ], "name": "getInterfaceImplementer", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "_contract", "type": "address" }, { "name": "_interfaceId", "type": "bytes4" } ], "name": "implementsERC165InterfaceNoCache", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "_contract", "type": "address" }, { "name": "_interfaceId", "type": "bytes4" } ], "name": "implementsERC165Interface", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "addr", "type": "address" }, { "indexed": true, "name": "interfaceHash", "type": "bytes32" }, { "indexed": true, "name": "implementer", "type": "address" } ], "name": "InterfaceImplementerSet", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "addr", "type": "address" }, { "indexed": true, "name": "newManager", "type": "address" } ], "name": "ManagerChanged", "type": "event" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/Faucet.json: -------------------------------------------------------------------------------- 1 | { "contractName": "Faucet", "abi": [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "name": "defaultAmount", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "defaultInterval", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "stateMutability": "payable", "type": "receive" }, { "inputs": [ { "internalType": "address", "name": "newManager", "type": "address" } ], "name": "changeManager", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "tokenContractAddress", "type": "address" } ], "name": "getClaimInterval", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "tokenContractAddress", "type": "address" } ], "name": "getClaimAmount", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "intervalSeconds", "type": "uint256" } ], "name": "setDefaultIntervalSeconds", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "setDefaultAmount", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "tokenContractAddress", "type": "address" }, { "internalType": "uint256", "name": "interval", "type": "uint256" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "setClaimSetting", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "tokenContractAddress", "type": "address" } ], "name": "claimToken", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "claimCfx", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/ParamsControl.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "uint64", "name": "vote_round", "type": "uint64" }, { "indexed": true, "internalType": "address", "name": "addr", "type": "address" }, { "indexed": true, "internalType": "uint16", "name": "topic_index", "type": "uint16" }, { "indexed": false, "internalType": "uint256[3]", "name": "votes", "type": "uint256[3]" } ], "name": "CastVote", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "uint64", "name": "vote_round", "type": "uint64" }, { "indexed": true, "internalType": "address", "name": "addr", "type": "address" }, { "indexed": true, "internalType": "uint16", "name": "topic_index", "type": "uint16" }, { "indexed": false, "internalType": "uint256[3]", "name": "votes", "type": "uint256[3]" } ], "name": "RevokeVote", "type": "event" }, { "inputs": [ { "internalType": "uint64", "name": "vote_round", "type": "uint64" }, { "components": [ { "internalType": "uint16", "name": "topic_index", "type": "uint16" }, { "internalType": "uint256[3]", "name": "votes", "type": "uint256[3]" } ], "internalType": "struct ParamsControl.Vote[]", "name": "vote_data", "type": "tuple[]" } ], "name": "castVote", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "currentRound", "outputs": [ { "internalType": "uint64", "name": "", "type": "uint64" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint64", "name": "", "type": "uint64" } ], "name": "posStakeForVotes", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "addr", "type": "address" } ], "name": "readVote", "outputs": [ { "components": [ { "internalType": "uint16", "name": "topic_index", "type": "uint16" }, { "internalType": "uint256[3]", "name": "votes", "type": "uint256[3]" } ], "internalType": "struct ParamsControl.Vote[]", "name": "", "type": "tuple[]" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint64", "name": "vote_round", "type": "uint64" } ], "name": "totalVotes", "outputs": [ { "components": [ { "internalType": "uint16", "name": "topic_index", "type": "uint16" }, { "internalType": "uint256[3]", "name": "votes", "type": "uint256[3]" } ], "internalType": "struct ParamsControl.Vote[]", "name": "", "type": "tuple[]" } ], "stateMutability": "view", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/PoSRegister.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "bytes32", "name": "identifier", "type": "bytes32" }, { "indexed": false, "internalType": "uint64", "name": "votePower", "type": "uint64" } ], "name": "IncreaseStake", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "bytes32", "name": "identifier", "type": "bytes32" }, { "indexed": false, "internalType": "bytes", "name": "blsPubKey", "type": "bytes" }, { "indexed": false, "internalType": "bytes", "name": "vrfPubKey", "type": "bytes" } ], "name": "Register", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "bytes32", "name": "identifier", "type": "bytes32" }, { "indexed": false, "internalType": "uint64", "name": "votePower", "type": "uint64" } ], "name": "Retire", "type": "event" }, { "inputs": [ { "internalType": "address", "name": "addr", "type": "address" } ], "name": "addressToIdentifier", "outputs": [ { "internalType": "bytes32", "name": "", "type": "bytes32" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "bytes32", "name": "identifier", "type": "bytes32" } ], "name": "getVotes", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "bytes32", "name": "identifier", "type": "bytes32" } ], "name": "identifierToAddress", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint64", "name": "votePower", "type": "uint64" } ], "name": "increaseStake", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "bytes32", "name": "identifier", "type": "bytes32" }, { "internalType": "uint64", "name": "votePower", "type": "uint64" }, { "internalType": "bytes", "name": "blsPubKey", "type": "bytes" }, { "internalType": "bytes", "name": "vrfPubKey", "type": "bytes" }, { "internalType": "bytes[2]", "name": "blsPubKeyProof", "type": "bytes[2]" } ], "name": "register", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint64", "name": "votes", "type": "uint64" } ], "name": "retire", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/RESOLVER.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "constant": true, "inputs": [ { "name": "interfaceID", "type": "bytes4" } ], "name": "supportsInterface", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "key", "type": "string" }, { "name": "value", "type": "string" } ], "name": "setText", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "contentTypes", "type": "uint256" } ], "name": "ABI", "outputs": [ { "name": "contentType", "type": "uint256" }, { "name": "data", "type": "bytes" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "x", "type": "bytes32" }, { "name": "y", "type": "bytes32" } ], "name": "setPubkey", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "node", "type": "bytes32" } ], "name": "content", "outputs": [ { "name": "ret", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "node", "type": "bytes32" } ], "name": "addr", "outputs": [ { "name": "ret", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "key", "type": "string" } ], "name": "text", "outputs": [ { "name": "ret", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "contentType", "type": "uint256" }, { "name": "data", "type": "bytes" } ], "name": "setABI", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "node", "type": "bytes32" } ], "name": "name", "outputs": [ { "name": "ret", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "name", "type": "string" } ], "name": "setName", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "hash", "type": "bytes32" } ], "name": "setContent", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "node", "type": "bytes32" } ], "name": "pubkey", "outputs": [ { "name": "x", "type": "bytes32" }, { "name": "y", "type": "bytes32" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "node", "type": "bytes32" }, { "name": "addr", "type": "address" } ], "name": "setAddr", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "bytes", "name": "name", "type": "bytes" }, { "internalType": "bytes", "name": "data", "type": "bytes" } ], "name": "resolve", "outputs": [ { "internalType": "bytes", "name": "", "type": "bytes" } ], "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "internalType": "bytes", "name": "response", "type": "bytes" }, { "internalType": "bytes", "name": "extraData", "type": "bytes" } ], "name": "resolveWithProof", "outputs": [ { "internalType": "bytes", "name": "", "type": "bytes" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "name": "ensAddr", "type": "address" } ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "node", "type": "bytes32" }, { "indexed": false, "name": "a", "type": "address" } ], "name": "AddrChanged", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "node", "type": "bytes32" }, { "indexed": false, "name": "hash", "type": "bytes32" } ], "name": "ContentChanged", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "node", "type": "bytes32" }, { "indexed": false, "name": "name", "type": "string" } ], "name": "NameChanged", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "node", "type": "bytes32" }, { "indexed": true, "name": "contentType", "type": "uint256" } ], "name": "ABIChanged", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "node", "type": "bytes32" }, { "indexed": false, "name": "x", "type": "bytes32" }, { "indexed": false, "name": "y", "type": "bytes32" } ], "name": "PubkeyChanged", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "node", "type": "bytes32" }, { "indexed": true, "name": "indexedKey", "type": "string" }, { "indexed": false, "name": "key", "type": "string" } ], "name": "TextChanged", "type": "event" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/SponsorWhitelistControl.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "inputs": [ { "internalType": "address[]", "name": "", "type": "address[]" } ], "name": "addPrivilege", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" }, { "internalType": "address[]", "name": "addresses", "type": "address[]" } ], "name": "addPrivilegeByAdmin", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "getSponsorForCollateral", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "getSponsorForGas", "outputs": [ { "internalType": "address", "name": "", "type": "address" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "getSponsoredBalanceForCollateral", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "getSponsoredBalanceForGas", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "getSponsoredGasFeeUpperBound", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "isAllWhitelisted", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" }, { "internalType": "address", "name": "user", "type": "address" } ], "name": "isWhitelisted", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address[]", "name": "", "type": "address[]" } ], "name": "removePrivilege", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" }, { "internalType": "address[]", "name": "addresses", "type": "address[]" } ], "name": "removePrivilegeByAdmin", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "setSponsorForCollateral", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" }, { "internalType": "uint256", "name": "upperBound", "type": "uint256" } ], "name": "setSponsorForGas", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "contractAddr", "type": "address" } ], "name": "getAvailableStoragePoints", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/contract/metadata/Staking.json: -------------------------------------------------------------------------------- 1 | { "abi": [ { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "deposit", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "user", "type": "address" }, { "internalType": "uint256", "name": "blockNumber", "type": "uint256" } ], "name": "getLockedStakingBalance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "user", "type": "address" } ], "name": "getStakingBalance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "user", "type": "address" }, { "internalType": "uint256", "name": "blockNumber", "type": "uint256" } ], "name": "getVotePower", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "uint256", "name": "unlockBlockNumber", "type": "uint256" } ], "name": "voteLock", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "withdraw", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ] } -------------------------------------------------------------------------------- /conflux_web3/dev/__init__.py: -------------------------------------------------------------------------------- 1 | from conflux_web3.main import Web3 2 | 3 | def get_local_web3(): 4 | return Web3(Web3.HTTPProvider("http://127.0.0.1:12537")) 5 | 6 | def get_testnet_web3(): 7 | return Web3(Web3.HTTPProvider("https://test.confluxrpc.com")) 8 | 9 | def get_mainnet_web3(): 10 | return Web3(Web3.HTTPProvider("https://main.confluxrpc.com")) 11 | -------------------------------------------------------------------------------- /conflux_web3/exceptions.py: -------------------------------------------------------------------------------- 1 | class NoWeb3Exception(Exception): 2 | pass 3 | 4 | class DisabledException(Exception): 5 | pass 6 | 7 | class DeploymentInfoNotFound(Exception): 8 | pass 9 | 10 | class ContractMetadataNotFound(Exception): 11 | pass 12 | 13 | class NameServiceNotSet(Exception): 14 | pass 15 | -------------------------------------------------------------------------------- /conflux_web3/method.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | Generic, 3 | Optional, 4 | Sequence, 5 | Callable, 6 | ) 7 | from web3.method import ( 8 | Method, 9 | Munger, 10 | TFunc 11 | ) 12 | from web3.types import ( 13 | RPCEndpoint, 14 | TReturn 15 | ) 16 | from conflux_web3._utils.method_formatters import ( 17 | cfx_request_formatters, 18 | cfx_result_formatters, 19 | ) 20 | 21 | 22 | class ConfluxMethod(Method[TFunc]): 23 | def __init__( 24 | self, 25 | json_rpc_method: Optional[RPCEndpoint] = None, 26 | mungers: Optional[Sequence[Munger]] = None, 27 | request_formatters: Optional[Callable[..., TReturn]] = None, 28 | result_formatters: Optional[Callable[..., TReturn]] = None, 29 | null_result_formatters: Optional[Callable[..., TReturn]] = None, 30 | method_choice_depends_on_args: Optional[Callable[..., RPCEndpoint]] = None, 31 | is_property: bool = False, 32 | ): 33 | request_formatters = request_formatters or cfx_request_formatters # type: ignore 34 | result_formatters = result_formatters or cfx_result_formatters # type: ignore 35 | Method.__init__(self, 36 | json_rpc_method, 37 | mungers, 38 | request_formatters, 39 | result_formatters, 40 | null_result_formatters, 41 | method_choice_depends_on_args, 42 | is_property 43 | ) -------------------------------------------------------------------------------- /conflux_web3/middleware/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, 3 | Sequence, 4 | Tuple, 5 | ) 6 | from conflux_web3.middleware.pending import ( 7 | PendingTransactionMiddleware 8 | ) 9 | from conflux_web3.middleware.wallet import ( 10 | Wallet, 11 | construct_sign_and_send_raw_middleware, 12 | ) 13 | from conflux_web3.middleware.names import ( 14 | name_to_address_middleware 15 | ) 16 | from conflux_web3.types import ( 17 | Middleware 18 | ) 19 | 20 | if TYPE_CHECKING: 21 | from conflux_web3 import Web3 22 | 23 | def conflux_default_middlewares(w3: "Web3") -> Sequence[Tuple[Middleware, str]]: 24 | return [ 25 | (name_to_address_middleware(w3), "name_to_address"), 26 | (PendingTransactionMiddleware, "PendingTransactionMiddleware"), 27 | (Wallet(), "wallet"), # type: ignore 28 | ] 29 | 30 | 31 | __all__ = [ 32 | "PendingTransactionMiddleware", 33 | "Wallet", 34 | "conflux_default_middlewares", 35 | "construct_sign_and_send_raw_middleware", 36 | ] 37 | -------------------------------------------------------------------------------- /conflux_web3/middleware/base.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from web3.middleware import Web3Middleware 3 | 4 | if TYPE_CHECKING: 5 | from conflux_web3 import Web3 6 | 7 | 8 | class ConfluxWeb3Middleware(Web3Middleware): 9 | _w3: "Web3" 10 | -------------------------------------------------------------------------------- /conflux_web3/middleware/names.py: -------------------------------------------------------------------------------- 1 | # a fork from web3.middleware.names 2 | from typing import ( 3 | TYPE_CHECKING, 4 | ) 5 | 6 | from web3._utils.rpc_abi import ( 7 | abi_request_formatters, 8 | ) 9 | from web3.middleware.formatting import ( 10 | FormattingMiddlewareBuilder, 11 | ) 12 | 13 | from conflux_web3.types import ( 14 | Middleware, 15 | ) 16 | from conflux_web3._utils.normalizers import ( 17 | abi_cns_resolver, 18 | ) 19 | from conflux_web3._utils.rpc_abi import ( 20 | RPC_ABIS, 21 | ) 22 | 23 | if TYPE_CHECKING: 24 | from conflux_web3 import Web3 # noqa: F401 25 | 26 | 27 | def name_to_address_middleware(w3: "Web3") -> Middleware: 28 | normalizers = [ 29 | abi_cns_resolver(w3), # type: ignore 30 | ] 31 | return FormattingMiddlewareBuilder.build( 32 | request_formatters=abi_request_formatters(normalizers, RPC_ABIS) # type: ignore 33 | ) 34 | -------------------------------------------------------------------------------- /conflux_web3/middleware/pending.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from web3.types import RPCEndpoint 3 | from conflux_web3._utils.rpc_abi import ( 4 | RPC 5 | ) 6 | from conflux_web3.types.transaction_hash import ( 7 | TransactionHash 8 | ) 9 | 10 | from conflux_web3.middleware.base import ConfluxWeb3Middleware 11 | 12 | class PendingTransactionMiddleware(ConfluxWeb3Middleware): 13 | def response_processor(self, method: RPCEndpoint, response: Any): 14 | if method == RPC.cfx_sendTransaction or method == RPC.cfx_sendRawTransaction: 15 | if "result" in response: 16 | transaction_hash = TransactionHash(response["result"]) 17 | transaction_hash.set_w3(self._w3) 18 | 19 | response["result"] = transaction_hash 20 | return response 21 | 22 | -------------------------------------------------------------------------------- /conflux_web3/providers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/conflux_web3/providers/__init__.py -------------------------------------------------------------------------------- /conflux_web3/providers/rpc.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Sequence, Type 2 | import time 3 | import requests 4 | from pydantic import BaseModel 5 | from web3.providers.rpc import HTTPProvider as OriHTTPProvider 6 | from web3.types import RPCEndpoint 7 | from web3._utils.empty import ( 8 | Empty, 9 | ) 10 | 11 | 12 | REQUEST_RETRY_DISALLOWLIST = [ 13 | "cfx_sendTransaction", 14 | "cfx_sendRawTransaction", 15 | ] 16 | 17 | def check_if_retry_on_failure( 18 | method: RPCEndpoint, 19 | allowlist: Optional[Sequence[str]] = None, 20 | disallowlist: Optional[Sequence[str]] = None, 21 | ) -> bool: 22 | if allowlist != None and disallowlist != None: 23 | raise ValueError("allowlist and disallowlist cannot be used together") 24 | elif allowlist is None and disallowlist is None: 25 | disallowlist = REQUEST_RETRY_DISALLOWLIST 26 | 27 | if allowlist is not None: 28 | if method in allowlist or method.split("_")[0] in allowlist: 29 | return True 30 | else: 31 | return False 32 | if disallowlist is not None: 33 | if method in disallowlist or method.split("_")[0] in disallowlist: 34 | return False 35 | else: 36 | return True 37 | 38 | # Should never reach here 39 | raise ValueError("allowlist or disallowlist must be provided") 40 | 41 | 42 | class ConfluxExceptionRetryConfiguration(BaseModel): 43 | errors: Sequence[Type[BaseException]] 44 | retries: int 45 | backoff_factor: float 46 | method_allowlist: Optional[Sequence[str]] 47 | method_disallowlist: Optional[Sequence[str]] 48 | 49 | def __init__( 50 | self, 51 | errors: Sequence[Type[BaseException]] = None, 52 | retries: int = 5, 53 | backoff_factor: float = 0.125, 54 | method_allowlist: Optional[Sequence[str]] = None, 55 | method_disallowlist: Optional[Sequence[str]] = None, 56 | ): 57 | super().__init__( 58 | errors=errors, 59 | retries=retries, 60 | backoff_factor=backoff_factor, 61 | method_allowlist=method_allowlist, 62 | method_disallowlist=method_disallowlist, 63 | ) 64 | 65 | class HTTPProvider(OriHTTPProvider): 66 | @property 67 | def exception_retry_configuration(self) -> ConfluxExceptionRetryConfiguration: 68 | if isinstance(self._exception_retry_configuration, Empty): 69 | self._exception_retry_configuration = ConfluxExceptionRetryConfiguration( 70 | errors=( 71 | ConnectionError, 72 | requests.HTTPError, 73 | requests.Timeout, 74 | ) 75 | ) 76 | return self._exception_retry_configuration 77 | 78 | def _make_request(self, method: RPCEndpoint, request_data: bytes) -> bytes: 79 | if ( 80 | self.exception_retry_configuration is not None 81 | and check_if_retry_on_failure( 82 | method, self.exception_retry_configuration.method_allowlist 83 | ) 84 | ): 85 | for i in range(self.exception_retry_configuration.retries): 86 | try: 87 | return self._request_session_manager.make_post_request( 88 | self.endpoint_uri, request_data, **self.get_request_kwargs() 89 | ) 90 | except tuple(self.exception_retry_configuration.errors) as e: 91 | if i < self.exception_retry_configuration.retries - 1: 92 | time.sleep( 93 | self.exception_retry_configuration.backoff_factor * 2**i 94 | ) 95 | continue 96 | else: 97 | raise e 98 | return None 99 | else: 100 | return self._request_session_manager.make_post_request( 101 | self.endpoint_uri, request_data, **self.get_request_kwargs() 102 | ) 103 | 104 | -------------------------------------------------------------------------------- /conflux_web3/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/conflux_web3/py.typed -------------------------------------------------------------------------------- /conflux_web3/txpool.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | Callable, 3 | Union 4 | ) 5 | 6 | from web3.module import Module 7 | from cfx_address import Base32Address 8 | 9 | from conflux_web3.method import ConfluxMethod 10 | from conflux_web3._utils.rpc_abi import RPC 11 | 12 | class Txpool(Module): 13 | _next_nonce: ConfluxMethod[Callable[[Union[Base32Address, str]], int]] = ConfluxMethod( 14 | RPC.txpool_nextNonce 15 | ) 16 | 17 | def next_nonce(self, address: Union[Base32Address, str]) -> int: 18 | return self._next_nonce(address) 19 | -------------------------------------------------------------------------------- /conflux_web3/types/transaction_hash.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | TYPE_CHECKING, Optional, Type, Union, cast 3 | ) 4 | from hexbytes import HexBytes 5 | from web3._utils.method_formatters import ( 6 | to_hexbytes 7 | ) 8 | 9 | from conflux_web3.exceptions import ( 10 | NoWeb3Exception 11 | ) 12 | 13 | if TYPE_CHECKING: 14 | from conflux_web3 import Web3 15 | from conflux_web3.types import ( 16 | TxReceipt, 17 | TxData, 18 | ) 19 | 20 | def requires_web3(func): 21 | def inner(self, *args, **kwargs): 22 | if self._w3 is None: 23 | raise NoWeb3Exception("No web3 instance is attached to transaction hash." 24 | "Use transactionHash.set_w3() to attach a w3 instance") 25 | return func(self, *args, **kwargs) 26 | return inner 27 | 28 | class TransactionHash(HexBytes): 29 | _w3: "Web3" = None # type: ignore 30 | 31 | def __new__(cls: Type[bytes], val: Union[bool, bytearray, bytes, int, str]) -> "TransactionHash": 32 | val = to_hexbytes(32, val) 33 | return cast(TransactionHash, super().__new__(cls, val)) # type: ignore 34 | 35 | def set_w3(self, w3: "Web3"): 36 | self._w3 = w3 37 | 38 | # @property 39 | # def status(self): 40 | # pass 41 | 42 | # TODO 43 | # def wait_till(self, target_status: str): 44 | # pass 45 | 46 | @requires_web3 47 | def mined(self, timeout: float = 60, poll_latency: float = 0.5) -> "TxData": 48 | return self._w3.cfx.wait_till_transaction_mined(self, timeout, poll_latency) 49 | 50 | @requires_web3 51 | def executed(self, timeout: float = 300, poll_latency: float = 0.5) -> "TxReceipt": 52 | return self._w3.cfx.wait_for_transaction_receipt(self, timeout, poll_latency) 53 | 54 | @requires_web3 55 | def confirmed(self, timeout: float = 600, poll_latency: float = 0.5) -> "TxReceipt": 56 | return self._w3.cfx.wait_till_transaction_confirmed(self, timeout, poll_latency) 57 | 58 | @requires_web3 59 | def finalized(self, timeout: float = 1200, poll_latency: float = 1) -> "TxReceipt": 60 | return self._w3.cfx.wait_till_transaction_finalized(self, timeout, poll_latency) 61 | 62 | def __repr__(self) -> str: 63 | return f"TransactionHash({self.hex()!r})" 64 | -------------------------------------------------------------------------------- /conflux_web3/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # export utils from _utils 2 | from conflux_web3._utils.transactions import ( 3 | fill_transaction_defaults 4 | ) 5 | 6 | __all__ = [ 7 | "fill_transaction_defaults" 8 | ] 9 | -------------------------------------------------------------------------------- /conflux_web3/utils/address.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | Union, 3 | overload, 4 | ) 5 | 6 | import rlp 7 | from hexbytes import HexBytes 8 | from eth_utils import ( 9 | keccak, 10 | to_bytes, 11 | to_checksum_address, 12 | ) 13 | 14 | from cfx_address import Base32Address 15 | from cfx_address.utils import ( 16 | normalize_to 17 | ) 18 | from cfx_utils.types import ( 19 | ChecksumAddress, 20 | ) 21 | 22 | def get_create_address(sender: Union[str, Base32Address], nonce: int, bytecode_hash: Union[bytes, str]) -> Union[ChecksumAddress, Base32Address]: 23 | """ 24 | Determine the resulting `CREATE` opcode contract address for a sender and a nonce. 25 | Typically, the sender is a wallet address and the nonce is the next nonce of the sender. 26 | NOTE: in Conflux, the contract address is computed differently from that in Ethereum 27 | where the bytecode hash is not accounted for in the address computation. 28 | 29 | :param sender: The address of the sender. Can be a hex string or a base32 address. 30 | :param nonce: The nonce of the sender. 31 | :param bytecode_hash: The keccak256 hash of the contract bytecode, whereas the "data" field of the transaction. Can be bytes or hex string. 32 | :return: The computed address as a hex string or base32 address, depending on the type of sender 33 | """ 34 | address_hex = normalize_to(sender, None) 35 | contract_address = "0x8" + keccak( 36 | b"\x00" 37 | + to_bytes(hexstr=address_hex) 38 | + nonce.to_bytes(32, "little") 39 | + HexBytes(bytecode_hash) 40 | ).hex()[-39:] 41 | if Base32Address.is_valid_base32(sender): 42 | return Base32Address(contract_address, network_id=Base32Address(sender).network_id) 43 | return to_checksum_address(contract_address) 44 | 45 | 46 | def get_create2_address( 47 | create2_factory_address: Union[str, Base32Address], salt: bytes, bytecode_hash: Union[bytes, str] 48 | ) -> Union[ChecksumAddress, Base32Address]: 49 | """ 50 | Compute the address of a contract created using CREATE2. 51 | 52 | :param create2_factory_address: The address of the CREATE2 factory contract. On Conflux, it is deployed via `CIP-31 `_ 53 | :param salt: A 32-byte value used as salt. Should be bytes. 54 | :param bytecode_hash: The keccak256 hash of the contract bytecode. Can be bytes or hex string. 55 | :return: The computed address as a hex string or base32 address, depending on the type of create2_factory_address 56 | """ 57 | address_hex = normalize_to(create2_factory_address, None) 58 | contract_address = "0x8" + keccak( 59 | b"\xff" 60 | + to_bytes(hexstr=address_hex) 61 | + salt 62 | + HexBytes(bytecode_hash) 63 | ).hex()[-39:] 64 | if Base32Address.is_valid_base32(create2_factory_address): 65 | return Base32Address(contract_address, network_id=Base32Address(create2_factory_address).network_id) 66 | return to_checksum_address(contract_address) 67 | -------------------------------------------------------------------------------- /docs/en/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | # Path to your Sphinx configuration file. 5 | configuration: docs/en/conf.py 6 | 7 | # Set the version of Python and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.10" 12 | jobs: 13 | pre_build: 14 | # Generate on-the-fly Sphinx configuration from Jupyter Book's _config.yml 15 | - "jupyter-book config sphinx docs/en" 16 | build: 17 | html: 18 | - "jupyter-book build docs/en" 19 | - "mkdir -p $READTHEDOCS_OUTPUT" 20 | - "cp -r docs/en/_build/* $READTHEDOCS_OUTPUT" 21 | 22 | 23 | python: 24 | install: 25 | - requirements: docs/requirements-doc.txt 26 | # - method: pip 27 | # path: ".[docs]" 28 | -------------------------------------------------------------------------------- /docs/en/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Logs 2 | 3 | ## 1.4.4 4 | 5 | * chore: pin typing-extensions to 4.13.2 6 | 7 | ## 1.4.3 8 | 9 | * chore: bump web3.py to 7.11.1 10 | 11 | ## 1.4.2 12 | 13 | * chore: bump web3.py to 7.10.0 14 | * chore: pin cfx-* package versions 15 | 16 | ## 1.4.1 17 | 18 | * fix: packing issues 19 | 20 | ## 1.4.0 21 | 22 | * bump web3.py to 7.8.0 23 | * middleware refactor with web3.py v6 -> v7 change 24 | * feat: provider supports default retry 25 | * feat: embedded contract name with type hints 26 | * feat: supports EIP-1559 27 | * chore: improved ci and docs 28 | * incompatible changes: fromEpoch/toEpoch -> from_epoch/to_epoch 29 | 30 | ## 1.3.0 31 | 32 | * bump web3.py to 6.20.x 33 | * deprecate python3.7 and support python3.13 34 | * signing middleware defaults to dynamic fee 35 | 36 | ## 1.2.5 37 | 38 | * feat: add `get_create_address`, `get_create2_address` in `web3.utils.address` 39 | 40 | ## 1.2.4 41 | 42 | * fix: `estimate_gas_and_collateral` with drip uint gas price 43 | 44 | ## 1.2.3 45 | 46 | * fix: dependency issues 47 | 48 | ## 1.2.2 49 | 50 | * update relying deps version 51 | 52 | ## 1.2.1 53 | 54 | * fix: missed interface(`getAvailableStoragePoints`) for internal contract `SponsorWhitelistControl` 55 | * RPC support: 56 | * `storagePointProp` field for `cfx_getParamsFromVote` return value 57 | * support python 3.12 58 | 59 | ## 1.2.0 60 | 61 | * bump web3.py to 6.8.0 62 | * remove cfxpm module 63 | * RPC support 64 | * filter rpcs 65 | * cfx_getEpochReceipts 66 | * CIP-107 RPC updates 67 | * missed get_admin interface 68 | 69 | ## 1.1.0 70 | 71 | * bump web3.py to 6.2.0 72 | 73 | ## 1.0.2 74 | 75 | * name service configuration 76 | 77 | ## 1.0.1 78 | 79 | * Add API documents 80 | * Support python 3.11 81 | 82 | ## 1.0.0 83 | 84 | * Release 85 | 86 | ## 1.0.0-dev8 87 | 88 | * fix: error caused by `find_functions_by_identifier` 89 | * add more strict format checking for `is_cns_name` 90 | 91 | ## 1.0.0-dev7 92 | 93 | * type hints: migrate to `Pylance`; add `py.typed` 94 | * fix: cns recursive import problem 95 | * feature: default network id by using `web3.address` 96 | * feature: support python3.7 97 | * doc: add inline documents for frequently used RPCs 98 | * change: recover `estimate_gas_and_collateral`, `gas_price` returned token units 99 | 100 | ## 1.0.0-dev6 101 | 102 | * bug fix: include the contract metadata in the release 103 | 104 | ## 1.0.0-dev4 105 | 106 | * Response containing "address" from rpc is wrapped by `Base32Address` 107 | * including contract return values 108 | * Introduces token unit `CFX`, `GDrip` and `Drip` 109 | * responses are formatted using Drip/GDrip/CFX 110 | * Drip/GDrip/CFX can be used as gas price or value 111 | * CNS support 112 | * available by using `w3.cns` or `w3.ens` 113 | * supports name service resolve as web3.py (as receiver/sender/parameter) 114 | * (unstable) supports `w3.cns.setup_address` and `w3.cns.setup_owner` 115 | 116 | ## 1.0.0-dev3 117 | 118 | * bump web3.py version 119 | * web3.py: 6.0.0b4->6.0.0b5 120 | * eth-account(required by cfx-account): ~=7.0.0 121 | 122 | ## 1.0.0-dev2 123 | 124 | * Optimizes web3.py hacking mechanisms 125 | * hacking codes are moved to `conflux_web3._hook.py` 126 | * Basic support for ethpm (furthur supports are not in plan) 127 | * The default account will be added to wallet if `w3.cfx.default_account` setter receives a `LocalAccount` object 128 | * `w3.cfx.default_account` is still a `Base32Address` 129 | * `.finalized()` apis are tests. 130 | * Fix contract constructor's `build_transaction()` bugs 131 | * Add `name` parameter to `w3.cfx.contract()` 132 | * supports internal contracts including `AdminControl`, `SponsorWhitelistControl`, `Staking`, `PoSRegister`, `CrossSpaceCall`, `ParamsControl` 133 | * supports specific contracts added in genesis block, including `Create2Factory`, `ERC1820` 134 | * supports `Faucet` in testnet 135 | * supports `cUSDT` and `FC` in mainnet and testnet 136 | -------------------------------------------------------------------------------- /docs/en/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/conflux-chain/python-conflux-sdk/dev?urlpath=tree/docs/en/examples/01-quickstart.ipynb) 4 | [![Documentation Status](https://readthedocs.org/projects/python-conflux-sdk/badge/?version=latest)](https://python-conflux-sdk.readthedocs.io/en/latest/?badge=latest) 5 | [![codecov](https://codecov.io/github/Conflux-Chain/python-conflux-sdk/branch/dev/graph/badge.svg?token=GZ62V9QW0A)](https://codecov.io/github/Conflux-Chain/python-conflux-sdk) 6 | ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/conflux-web3) 7 | 8 | [README](https://python-conflux-sdk.readthedocs.io/en/latest/README.html) | [中文文档](https://python-conflux-sdk.readthedocs.io/zh_CN/latest/README.html) 9 | 10 | - [Introduction](#introduction) 11 | - [Overview](#overview) 12 | - [Quickstart](#quickstart) 13 | - [Documentations](#documentations) 14 | - [Run Code Examples Online!](#run-code-examples-online) 15 | 16 | 17 | ## Overview 18 | 19 | Python-conflux-sdk (or conflux-web3) helps to interact with Conflux network using python. It is built over [web3.py](https://github.com/ethereum/web3.py) and most of its APIs are consistent with [web3.py](https://github.com/ethereum/web3.py). 20 | 21 | > Note: python-conflux-sdk v1.3.0 is the version compatible with web3.py v6.x. If you are using web3.py v7.x, please use the v1.4.0 or later. 22 | 23 | ## Quickstart 24 | 25 | Requirements: python version >= 3.8 26 | 27 | ```bash 28 | $ pip3 install conflux-web3 29 | ``` 30 | 31 | ```python 32 | from conflux_web3 import Web3 33 | 34 | w3 = Web3(Web3.HTTPProvider("https://test.confluxrpc.com")) 35 | 36 | acct = w3.account.from_key("0xxxxxxxxxxxxxx") 37 | w3.cfx.default_account = acct 38 | 39 | # Uncomment the following line if there is not CFX in your account 40 | # w3.cfx.contract(name="Faucet").claimCfx().transact().executed() 41 | 42 | w3.cfx.send_transaction({ 43 | 'to': w3.address.zero_address(), 44 | 'value': 10**18, 45 | }).executed() 46 | ``` 47 | 48 | Or you can also use API as you do in `web3.py` before v6.20: 49 | 50 | ``` python 51 | # modified from https://web3py.readthedocs.io/en/v6.20.2/transactions.html#chapter-1-w3-eth-send-transaction-signer-middleware 52 | from conflux_web3 import Web3 53 | w3 = Web3(Web3.HTTPProvider("https://test.confluxrpc.com")) 54 | from conflux_web3.middleware import SignAndSendRawMiddlewareBuilder 55 | from cfx_account import Account 56 | acct = Account.create('KEYSMASH FJAFJKLDSKF7JKFDJ 1530', network_id=w3.cfx.chain_id) 57 | w3.middleware_onion.inject(SignAndSendRawMiddlewareBuilder.build(acct), layer=0) 58 | w3.cfx.default_account = acct.address 59 | 60 | transaction = { 61 | 'to': w3.address.zero_address(), 62 | 'value': 22, 63 | } 64 | w3.cfx.send_transaction(transaction) 65 | ``` 66 | 67 | ## Documentations 68 | 69 | More detailed code examples are provided in the [documentation](https://python-conflux-sdk.readthedocs.io/en/latest/README.html). 70 | 71 | ### Run Code Examples Online! 72 | 73 | All code examples can be run online in [mybinder](https://mybinder.org/). You can click `🚀` -> `Binder` on the top bar to activate the running environment. All dependencies wil be installed and the example can be run immediately. 74 | -------------------------------------------------------------------------------- /docs/en/_config.yml: -------------------------------------------------------------------------------- 1 | # Book settings 2 | # Learn more at https://jupyterbook.org/customize/config.html 3 | # Comprehensive example: https://github.com/executablebooks/jupyter-book/blob/master/docs/_config.yml 4 | 5 | title: python-conflux-sdk 6 | author: Conflux 7 | logo: ../logo.png 8 | 9 | # Force re-execution of notebooks on each build. 10 | # See https://jupyterbook.org/content/execute.html 11 | execute: 12 | execute_notebooks: 'off' 13 | 14 | # Information about where the book exists on the web 15 | repository: 16 | url: https://github.com/conflux-chain/python-conflux-sdk # Online location of your book 17 | path_to_book: docs/en # Optional path to your book, relative to the repository root 18 | branch: dev # Which branch of the repository should be used when creating links (optional) 19 | 20 | # Add GitHub buttons to your book 21 | # See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository 22 | html: 23 | use_issues_button: true 24 | use_repository_button: true 25 | 26 | launch_buttons: 27 | binderhub_url: "https://mybinder.org" 28 | colab_url: "https://colab.research.google.com" 29 | thebe: true 30 | 31 | sphinx: 32 | extra_extensions: 33 | - 'sphinx.ext.autodoc' 34 | - 'sphinx.ext.viewcode' 35 | - 'sphinx.ext.napoleon' 36 | - 'sphinx.ext.doctest' 37 | - 'IPython.sphinxext.ipython_console_highlighting' 38 | - 'sphinx_autodoc_typehints' 39 | # - 'sphinx.ext.intersphinx' 40 | # - 'sphinx.ext.autosummary' 41 | config: 42 | # autosummary_generate: True 43 | autoclass_content: "both" 44 | # autodoc_typehints: 'description' 45 | copyright: "2025" 46 | # autodoc_type_aliases: { 47 | # 'TxParam': 'cfx_utils.types.TxParam' 48 | # } 49 | typehints_fully_qualified: true 50 | # napoleon_use_param: true 51 | # always_document_param_types: true 52 | # napoleon_preprocess_types: true 53 | typehints_use_signature: true 54 | typehints_use_signature_return: true 55 | always_use_bars_union: true 56 | -------------------------------------------------------------------------------- /docs/en/_toc.yml: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | # Learn more at https://jupyterbook.org/customize/toc.html 3 | 4 | format: jb-book 5 | root: README 6 | parts: 7 | - caption: Examples 8 | chapters: 9 | - file: examples/01-quickstart 10 | - file: examples/02-address_account_and_wallet 11 | - file: examples/03-trace_transaction_status 12 | - file: examples/04-query_data_via_RPC 13 | - file: examples/05-interact_with_contracts_and_process_logs 14 | - file: examples/10-send_raw_transaction 15 | - file: examples/11-construct_transaction_from_scratch 16 | - file: examples/12-sign_data_other_than_transactions 17 | - caption: API References 18 | chapters: 19 | - file: api 20 | - file: types 21 | - file: util_types 22 | - file: token_unit 23 | - file: cfx_address 24 | - file: cfx_address.utils 25 | - file: cfx_account 26 | -------------------------------------------------------------------------------- /docs/en/api.rst: -------------------------------------------------------------------------------- 1 | conflux_web3.cfx apis 2 | ====================== 3 | 4 | .. autoclass:: conflux_web3.client.BaseCfx 5 | :members: 6 | 7 | .. autoclass:: conflux_web3.client.ConfluxClient 8 | :members: 9 | :undoc-members: 10 | -------------------------------------------------------------------------------- /docs/en/cfx_account.rst: -------------------------------------------------------------------------------- 1 | cfx_account 2 | ============= 3 | 4 | .. automodule:: cfx_account.account 5 | :members: 6 | :undoc-members: 7 | 8 | .. automodule:: cfx_account.signers.local 9 | :members: 10 | :undoc-members: 11 | -------------------------------------------------------------------------------- /docs/en/cfx_address.rst: -------------------------------------------------------------------------------- 1 | Base32Address 2 | ============= 3 | 4 | .. autoclass:: cfx_address.address.Base32Address 5 | :members: 6 | :undoc-members: 7 | :special-members: __eq__ 8 | :member-order: groupwise 9 | 10 | .. automethod:: cfx_address.address.get_base32_address_factory 11 | -------------------------------------------------------------------------------- /docs/en/cfx_address.utils.rst: -------------------------------------------------------------------------------- 1 | cfx_address.utils 2 | ================== 3 | 4 | .. automodule:: cfx_address.utils 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/en/conf.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Auto-generated by `jupyter-book config` 3 | # If you wish to continue using _config.yml, make edits to that file and 4 | # re-generate this one. 5 | ############################################################################### 6 | always_use_bars_union = True 7 | author = 'Conflux' 8 | autoclass_content = 'both' 9 | comments_config = {'hypothesis': False, 'utterances': False} 10 | copyright = '2025' 11 | exclude_patterns = ['**.ipynb_checkpoints', '.DS_Store', 'Thumbs.db', '_build'] 12 | extensions = ['sphinx_togglebutton', 'sphinx_copybutton', 'myst_nb', 'jupyter_book', 'sphinx_thebe', 'sphinx_comments', 'sphinx_external_toc', 'sphinx.ext.intersphinx', 'sphinx_design', 'sphinx_book_theme', 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'sphinx.ext.doctest', 'IPython.sphinxext.ipython_console_highlighting', 'sphinx_autodoc_typehints', 'sphinx_jupyterbook_latex', 'sphinx_multitoc_numbering'] 13 | external_toc_exclude_missing = False 14 | external_toc_path = '_toc.yml' 15 | html_baseurl = '' 16 | html_favicon = '' 17 | html_logo = '../logo.png' 18 | html_sourcelink_suffix = '' 19 | html_theme = 'sphinx_book_theme' 20 | html_theme_options = {'search_bar_text': 'Search this book...', 'launch_buttons': {'notebook_interface': 'classic', 'binderhub_url': '', 'jupyterhub_url': '', 'thebe': False, 'colab_url': '', 'deepnote_url': ''}, 'path_to_docs': 'docs/en', 'repository_url': 'https://github.com/conflux-chain/python-conflux-sdk', 'repository_branch': 'dev', 'extra_footer': '', 'home_page_in_toc': True, 'announcement': '', 'analytics': {'google_analytics_id': '', 'plausible_analytics_domain': '', 'plausible_analytics_url': 'https://plausible.io/js/script.js'}, 'use_repository_button': True, 'use_edit_page_button': False, 'use_issues_button': True} 21 | html_title = 'python-conflux-sdk' 22 | latex_engine = 'pdflatex' 23 | myst_enable_extensions = ['colon_fence', 'dollarmath', 'linkify', 'substitution', 'tasklist'] 24 | myst_url_schemes = ['mailto', 'http', 'https'] 25 | nb_execution_allow_errors = False 26 | nb_execution_cache_path = '' 27 | nb_execution_excludepatterns = [] 28 | nb_execution_in_temp = False 29 | nb_execution_mode = 'off' 30 | nb_execution_timeout = 30 31 | nb_output_stderr = 'show' 32 | numfig = True 33 | pygments_style = 'sphinx' 34 | suppress_warnings = ['myst.domains'] 35 | typehints_fully_qualified = True 36 | typehints_use_signature = True 37 | typehints_use_signature_return = True 38 | use_jupyterbook_latex = True 39 | use_multitoc_numbering = True 40 | -------------------------------------------------------------------------------- /docs/en/examples/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/docs/en/examples/.gitkeep -------------------------------------------------------------------------------- /docs/en/examples/03-trace_transaction_status.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Trace Transaction Status\n", 8 | "\n", 9 | "Run this example online by clicking `\ud83d\ude80` -> `Binder` on the top bar!\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Preparation\n" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "# preparation: init w3 instance and prepare transaction hash object tx_hash\n", 26 | "from conflux_web3 import Web3\n", 27 | "\n", 28 | "w3 = Web3(Web3.HTTPProvider(\"https://test.confluxrpc.com\"))\n", 29 | "acct = w3.account.create()\n", 30 | "w3.cfx.default_account = acct\n", 31 | "faucet = w3.cfx.contract(name=\"Faucet\")\n", 32 | "\n", 33 | "# get a tx_hash\n", 34 | "tx_hash = faucet.functions.claimCfx().transact()" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "## Status of a Transaction in Conflux\n", 42 | "\n", 43 | "In Conflux, transaction will witness several periods after it is sent:\n", 44 | "\n", 45 | "1. `pending`: the transaction is sent, but not contained in any block\n", 46 | "2. `mined`: the transaction is already contained in a block, but might not be executed\n", 47 | "3. `executed`: the transaction is executed\n", 48 | "4. `confirmed`: the transaction is confirmed under PoW chain confirmation rule, which means it is extremly unlikely to be reverted unless the PoW chain is under attack.\n", 49 | "5. `finalized`: the transaction is **finalized** by PoS chain, which means the transaction is impossible to revert, but it would take 4~6 minutes before a transaction is finalized.\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Trace the Status of a Transaction\n", 57 | "\n", 58 | "We can use `w3.cfx.wait_till_transaction_mined`, `w3.cfx.wait_till_transaction_executed`, `w3.cfx.wait_till_transaction_confirmed`, `w3.cfx.wait_till_transaction_finalized` to wait until transaction reached specific status.\n" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 2, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "w3.cfx.wait_till_transaction_mined(tx_hash)\n", 68 | "w3.cfx.wait_till_transaction_executed(tx_hash)\n", 69 | "w3.cfx.wait_till_transaction_confirmed(tx_hash)\n", 70 | "if False: # 4\uff5e6 minutes is needed\n", 71 | " w3.cfx.wait_till_transaction_finalized(tx_hash)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "### Syntactic Sugar\n", 79 | "\n", 80 | "We often need to trace the status of the sent transaction, so SDK wraps the returned hex transaction hash from `send_transacation` or `send_raw_transaction` to visit the above APIs easily.\n" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 3, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "tx_hash.mined()\n", 90 | "tx_hash.executed()\n", 91 | "tx_hash.confirmed()\n", 92 | "if False:\n", 93 | " tx_hash.finalized()" 94 | ] 95 | } 96 | ], 97 | "metadata": { 98 | "kernelspec": { 99 | "display_name": "Python 3.9.13 ('sdk')", 100 | "language": "python", 101 | "name": "python3" 102 | }, 103 | "language_info": { 104 | "codemirror_mode": { 105 | "name": "ipython", 106 | "version": 3 107 | }, 108 | "file_extension": ".py", 109 | "mimetype": "text/x-python", 110 | "name": "python", 111 | "nbconvert_exporter": "python", 112 | "pygments_lexer": "ipython3", 113 | "version": "3.9.13" 114 | }, 115 | "orig_nbformat": 4, 116 | "vscode": { 117 | "interpreter": { 118 | "hash": "a3057bbf184bca29acc6a0538c16f383babaf4f383bee8205e8e826137244d0e" 119 | } 120 | } 121 | }, 122 | "nbformat": 4, 123 | "nbformat_minor": 2 124 | } -------------------------------------------------------------------------------- /docs/en/examples/11-construct_transaction_from_scratch.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# Construct Transaction from Scratch\n", 9 | "\n", 10 | "Run this example online by clicking `\ud83d\ude80` -> `Binder` on the top bar!\n" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "from conflux_web3 import Web3\n", 20 | "\n", 21 | "w3 = Web3(Web3.HTTPProvider(\"https://test.confluxrpc.com\"))\n", 22 | "account = w3.account.from_key(\"0x....\") # fill your secret key here" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "The meaning of each field is expalained [here](https://doc.confluxnetwork.org/docs/core/core-space-basics/transactions/tx-fields). And here presents how to choose each field.\n" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "prebuilt_tx = {\n", 39 | " 'from': account.address,\n", 40 | " 'nonce': w3.cfx.get_next_nonce(account.address),\n", 41 | " 'to': w3.account.create().address,\n", 42 | " 'value': 100,\n", 43 | " 'gasPrice': w3.cfx.gas_price,\n", 44 | " 'chainId': w3.cfx.chain_id,\n", 45 | " # 'gas': 21000, \n", 46 | " # 'storageLimit': 0,\n", 47 | " 'epochHeight': w3.cfx.epoch_number\n", 48 | "}\n", 49 | "\n", 50 | "# estimate\n", 51 | "estimate_result = w3.cfx.estimate_gas_and_collateral(prebuilt_tx)\n", 52 | "\n", 53 | "prebuilt_tx['gas'] = estimate_result['gasLimit']\n", 54 | "prebuilt_tx['storageLimit'] = estimate_result['storageCollateralized']\n", 55 | "\n", 56 | "w3.cfx.send_raw_transaction(\n", 57 | " account.sign_transaction(prebuilt_tx).raw_transaction \n", 58 | ").executed()" 59 | ] 60 | } 61 | ], 62 | "metadata": { 63 | "kernelspec": { 64 | "display_name": "Python 3.9.13 ('sdk')", 65 | "language": "python", 66 | "name": "python3" 67 | }, 68 | "language_info": { 69 | "codemirror_mode": { 70 | "name": "ipython", 71 | "version": 3 72 | }, 73 | "file_extension": ".py", 74 | "mimetype": "text/x-python", 75 | "name": "python", 76 | "nbconvert_exporter": "python", 77 | "pygments_lexer": "ipython3", 78 | "version": "3.12.7" 79 | }, 80 | "orig_nbformat": 4, 81 | "vscode": { 82 | "interpreter": { 83 | "hash": "a3057bbf184bca29acc6a0538c16f383babaf4f383bee8205e8e826137244d0e" 84 | } 85 | } 86 | }, 87 | "nbformat": 4, 88 | "nbformat_minor": 2 89 | } -------------------------------------------------------------------------------- /docs/en/token_unit.rst: -------------------------------------------------------------------------------- 1 | cfx_utils.token_unit 2 | ==================== 3 | 4 | A module providing methods to operate token units in Conflux. 5 | 6 | Currently :class:`cfx_utils.token_unit.Drip`, :class:`cfx_utils.token_unit.CFX` and :class:`cfx_utils.token_unit.GDrip` 7 | can be imported from :mod:`cfx_utils.token_unit`. These classes inherits from `cfx_utils.token_unit.AbstractTokenUnit` and 8 | support computing or comparing:: 9 | 10 | >>> from cfx_utils.token_unit import CFX, Drip 11 | >>> CFX(1) 12 | 1 CFX 13 | >>> CFX(1).value 14 | 1 15 | >>> Drip(1) 16 | 1 Drip 17 | >>> CFX(1) == Drip(1) * 10**18 18 | True 19 | >>> Drip(1) * 2 20 | 2 Drip 21 | >>> CFX(1) / Drip(1) 22 | Decimal('1000000000000000000') 23 | >>> Drip(1) / 2 24 | Traceback (most recent call last): 25 | ... 26 | cfx_utils.exceptions.InvalidTokenOperation: ... 27 | 28 | .. autoclass:: cfx_utils.token_unit.Drip 29 | :members: 30 | :undoc-members: 31 | :inherited-members: 32 | :private-members: _decimals, _base_unit 33 | 34 | .. autoclass:: cfx_utils.token_unit.CFX 35 | :members: 36 | :undoc-members: 37 | :inherited-members: 38 | :private-members: _decimals, _base_unit 39 | 40 | .. autoclass:: cfx_utils.token_unit.AbstractTokenUnit 41 | :members: 42 | :special-members: 43 | :member-order: bysource 44 | :exclude-members: __weakref__ 45 | 46 | .. automethod:: cfx_utils.token_unit.to_int_if_drip_units 47 | -------------------------------------------------------------------------------- /docs/en/types.rst: -------------------------------------------------------------------------------- 1 | conflux_web3.types 2 | ================== 3 | 4 | .. automodule:: conflux_web3.types 5 | :members: 6 | :undoc-members: 7 | :exclude-members: Drip 8 | 9 | 10 | .. autodata:: conflux_web3.types.TxParam 11 | -------------------------------------------------------------------------------- /docs/en/util_types.rst: -------------------------------------------------------------------------------- 1 | cfx_utils.types 2 | =============== 3 | 4 | .. automodule:: cfx_utils.types 5 | :members: 6 | :undoc-members: 7 | :exclude-members: Drip, CFX, AbstractDerivedTokenUnit 8 | 9 | .. autodata:: cfx_utils.types.EpochLiteral 10 | -------------------------------------------------------------------------------- /docs/examples/base/03-trace_transaction_status.base.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | "\n", 9 | "" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "\n", 17 | "# Trace Transaction Status\n", 18 | "\n", 19 | "Run this example online by clicking `🚀` -> `Binder` on the top bar!\n", 20 | "\n", 21 | "# 追踪交易状态\n", 22 | "\n", 23 | "点击顶部栏的 `🚀` -> `Binder` 在线运行此示例!" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "\n", 31 | "## Preparation\n", 32 | "\n", 33 | "## 准备工作" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 1, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "# preparation: init w3 instance and prepare transaction hash object tx_hash\n", 43 | "from conflux_web3 import Web3\n", 44 | "\n", 45 | "w3 = Web3(Web3.HTTPProvider(\"https://test.confluxrpc.com\"))\n", 46 | "acct = w3.account.create()\n", 47 | "w3.cfx.default_account = acct\n", 48 | "faucet = w3.cfx.contract(name=\"Faucet\")\n", 49 | "\n", 50 | "# get a tx_hash\n", 51 | "tx_hash = faucet.functions.claimCfx().transact()" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "\n", 59 | "## Status of a Transaction in Conflux\n", 60 | "\n", 61 | "In Conflux, transaction will witness several periods after it is sent:\n", 62 | "\n", 63 | "1. `pending`: the transaction is sent, but not contained in any block\n", 64 | "2. `mined`: the transaction is already contained in a block, but might not be executed\n", 65 | "3. `executed`: the transaction is executed\n", 66 | "4. `confirmed`: the transaction is confirmed under PoW chain confirmation rule, which means it is extremly unlikely to be reverted unless the PoW chain is under attack.\n", 67 | "5. `finalized`: the transaction is **finalized** by PoS chain, which means the transaction is impossible to revert, but it would take 4~6 minutes before a transaction is finalized.\n", 68 | "\n", 69 | "## Conflux 中的交易状态\n", 70 | "\n", 71 | "在 Conflux 中,交易发送后会经历以下几个阶段:\n", 72 | "\n", 73 | "1. `pending`:交易已发送,但尚未被打包进区块\n", 74 | "2. `mined`:交易已被打包进区块,但可能尚未执行\n", 75 | "3. `executed`:交易已执行完成\n", 76 | "4. `confirmed`:交易已经通过 PoW 链的确认规则,这意味着除非 PoW 链遭受攻击,否则交易几乎不可能被回滚\n", 77 | "5. `finalized`:交易已被 PoS 链**最终确认**,这意味着交易不可能被回滚,但通常需要 4~6 分钟才能达到这个状态" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "\n", 85 | "## Trace the Status of a Transaction\n", 86 | "\n", 87 | "We can use `w3.cfx.wait_till_transaction_mined`, `w3.cfx.wait_till_transaction_executed`, `w3.cfx.wait_till_transaction_confirmed`, `w3.cfx.wait_till_transaction_finalized` to wait until transaction reached specific status.\n", 88 | "\n", 89 | "## 追踪交易状态\n", 90 | "\n", 91 | "我们可以使用 `w3.cfx.wait_till_transaction_mined`、`w3.cfx.wait_till_transaction_executed`、`w3.cfx.wait_till_transaction_confirmed`、`w3.cfx.wait_till_transaction_finalized` 等方法来等待交易达到特定状态。" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 2, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "w3.cfx.wait_till_transaction_mined(tx_hash)\n", 101 | "w3.cfx.wait_till_transaction_executed(tx_hash)\n", 102 | "w3.cfx.wait_till_transaction_confirmed(tx_hash)\n", 103 | "if False: # 4~6 minutes is needed\n", 104 | " w3.cfx.wait_till_transaction_finalized(tx_hash)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "\n", 112 | "### Syntactic Sugar\n", 113 | "\n", 114 | "We often need to trace the status of the sent transaction, so SDK wraps the returned hex transaction hash from `send_transacation` or `send_raw_transaction` to visit the above APIs easily.\n", 115 | "\n", 116 | "### 语法糖\n", 117 | "\n", 118 | "我们经常需要追踪已发送交易的状态,因此 SDK 对 `send_transaction` 或 `send_raw_transaction` 返回的十六进制交易哈希进行了封装,使其能够方便地访问上述 API。" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 3, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "tx_hash.mined()\n", 128 | "tx_hash.executed()\n", 129 | "tx_hash.confirmed()\n", 130 | "if False:\n", 131 | " tx_hash.finalized()" 132 | ] 133 | } 134 | ], 135 | "metadata": { 136 | "kernelspec": { 137 | "display_name": "Python 3.9.13 ('sdk')", 138 | "language": "python", 139 | "name": "python3" 140 | }, 141 | "language_info": { 142 | "codemirror_mode": { 143 | "name": "ipython", 144 | "version": 3 145 | }, 146 | "file_extension": ".py", 147 | "mimetype": "text/x-python", 148 | "name": "python", 149 | "nbconvert_exporter": "python", 150 | "pygments_lexer": "ipython3", 151 | "version": "3.9.13" 152 | }, 153 | "orig_nbformat": 4, 154 | "vscode": { 155 | "interpreter": { 156 | "hash": "a3057bbf184bca29acc6a0538c16f383babaf4f383bee8205e8e826137244d0e" 157 | } 158 | } 159 | }, 160 | "nbformat": 4, 161 | "nbformat_minor": 2 162 | } 163 | -------------------------------------------------------------------------------- /docs/examples/base/11-construct_transaction_from_scratch.base.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\n", 8 | "\n", 9 | "" 10 | ] 11 | }, 12 | { 13 | "attachments": {}, 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "\n", 18 | "# Construct Transaction from Scratch\n", 19 | "\n", 20 | "Run this example online by clicking `🚀` -> `Binder` on the top bar!\n", 21 | "\n", 22 | "# 从零构建交易\n", 23 | "\n", 24 | "点击顶部栏的 `🚀` -> `Binder` 在线运行此示例!" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 1, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "from conflux_web3 import Web3\n", 34 | "\n", 35 | "w3 = Web3(Web3.HTTPProvider(\"https://test.confluxrpc.com\"))\n", 36 | "account = w3.account.from_key(\"0x....\") # fill your secret key here" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "\n", 44 | "The meaning of each field is expalained [here](https://doc.confluxnetwork.org/docs/core/core-space-basics/transactions/tx-fields). And here presents how to choose each field.\n", 45 | "\n", 46 | "每个字段的含义在[这里](https://doc.confluxnetwork.org/docs/core/core-space-basics/transactions/tx-fields)有详细说明。下面展示如何选择每个字段的值。" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "prebuilt_tx = {\n", 56 | " 'from': account.address,\n", 57 | " 'nonce': w3.cfx.get_next_nonce(account.address),\n", 58 | " 'to': w3.account.create().address,\n", 59 | " 'value': 100,\n", 60 | " 'gasPrice': w3.cfx.gas_price,\n", 61 | " 'chainId': w3.cfx.chain_id,\n", 62 | " # 'gas': 21000, \n", 63 | " # 'storageLimit': 0,\n", 64 | " 'epochHeight': w3.cfx.epoch_number\n", 65 | "}\n", 66 | "\n", 67 | "# estimate\n", 68 | "estimate_result = w3.cfx.estimate_gas_and_collateral(prebuilt_tx)\n", 69 | "\n", 70 | "prebuilt_tx['gas'] = estimate_result['gasLimit']\n", 71 | "prebuilt_tx['storageLimit'] = estimate_result['storageCollateralized']\n", 72 | "\n", 73 | "w3.cfx.send_raw_transaction(\n", 74 | " account.sign_transaction(prebuilt_tx).raw_transaction \n", 75 | ").executed()" 76 | ] 77 | } 78 | ], 79 | "metadata": { 80 | "kernelspec": { 81 | "display_name": "Python 3.9.13 ('sdk')", 82 | "language": "python", 83 | "name": "python3" 84 | }, 85 | "language_info": { 86 | "codemirror_mode": { 87 | "name": "ipython", 88 | "version": 3 89 | }, 90 | "file_extension": ".py", 91 | "mimetype": "text/x-python", 92 | "name": "python", 93 | "nbconvert_exporter": "python", 94 | "pygments_lexer": "ipython3", 95 | "version": "3.12.7" 96 | }, 97 | "orig_nbformat": 4, 98 | "vscode": { 99 | "interpreter": { 100 | "hash": "a3057bbf184bca29acc6a0538c16f383babaf4f383bee8205e8e826137244d0e" 101 | } 102 | } 103 | }, 104 | "nbformat": 4, 105 | "nbformat_minor": 2 106 | } 107 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/docs/logo.png -------------------------------------------------------------------------------- /docs/requirements-doc.txt: -------------------------------------------------------------------------------- 1 | jupyter-book 2 | conflux-web3 3 | cfx-utils>=1.0.1 4 | sphinx-autodoc-typehints 5 | # sphinx-design -------------------------------------------------------------------------------- /docs/zh-CN/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | sphinx: 4 | # Path to your Sphinx configuration file. 5 | configuration: docs/zh-CN/conf.py 6 | 7 | # Set the version of Python and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.10" 12 | jobs: 13 | pre_build: 14 | # Generate on-the-fly Sphinx configuration from Jupyter Book's _config.yml 15 | - "jupyter-book config sphinx docs/zh-CN" 16 | - "jupyter-book build docs/zh-CN" 17 | build: 18 | html: 19 | - "jupyter-book build docs/zh-CN" 20 | - "mkdir -p $READTHEDOCS_OUTPUT" 21 | - "cp -r docs/zh-CN/_build/* $READTHEDOCS_OUTPUT" 22 | 23 | python: 24 | install: 25 | - requirements: docs/requirements-doc.txt 26 | # - method: pip 27 | # path: ".[docs]" 28 | -------------------------------------------------------------------------------- /docs/zh-CN/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/conflux-chain/python-conflux-sdk/dev?urlpath=tree/docs/en/examples/01-quickstart.ipynb) 4 | [![Documentation Status](https://readthedocs.org/projects/python-conflux-sdk/badge/?version=latest)](https://python-conflux-sdk.readthedocs.io/en/latest/?badge=latest) 5 | 6 | [README](https://python-conflux-sdk.readthedocs.io/en/latest/README.html) | [中文文档](https://python-conflux-sdk.readthedocs.io/zh_CN/latest/README.html) 7 | 8 | - [Introduction](#introduction) 9 | - [概览](#概览) 10 | - [Quickstart](#quickstart) 11 | - [文档](#文档) 12 | - [在线运行示例代码](#在线运行示例代码) 13 | 14 | 15 | ## 概览 16 | 17 | Python-conflux-sdk 帮助开发者使用 python 与 Conflux 区块链交互,本库基于 [web3.py](https://github.com/ethereum/web3.py) 构建且大部分 API 与 `web3.py` 兼容。 18 | 19 | ## Quickstart 20 | 21 | 安装需求: python >= 3.8 22 | 23 | ```bash 24 | pip3 install conflux-web3 25 | ``` 26 | 27 | ```python 28 | from conflux_web3 import Web3 29 | 30 | w3 = Web3(Web3.HTTPProvider("https://test.confluxrpc.com")) 31 | 32 | acct = w3.account.from_key("0xxxxxxxxxxxxxx") 33 | w3.cfx.default_account = acct 34 | w3.cfx.contract(name="Faucet").claimCfx().transact().executed() 35 | 36 | w3.cfx.send_transaction({ 37 | 'to': w3.address.zero_address(), 38 | 'value': 10**18, 39 | }).executed() 40 | ``` 41 | 42 | 您也可以按照 `web3.py` 的 API 风格使用本 SDK: 43 | 44 | ``` python 45 | # 由 https://web3py.readthedocs.io/en/stable/middleware.html#signing 修改而来 46 | from conflux_web3 import Web3 47 | w3 = Web3("https://test.confluxrpc.com") 48 | from conflux_web3.middleware import construct_sign_and_send_raw_middleware 49 | from cfx_account import Account 50 | acct = Account.create('KEYSMASH FJAFJKLDSKF7JKFDJ 1530') 51 | w3.middleware_onion.add(construct_sign_and_send_raw_middleware(acct)) 52 | w3.cfx.default_account = acct.address 53 | 54 | transaction = { 55 | 'to': w3.address.zero_address(), 56 | 'value': 22, 57 | } 58 | w3.cfx.send_transaction(transaction) 59 | ``` 60 | 61 | ## 文档 62 | 63 | 更详细的文档与用例可以参考 [文档](https://python-conflux-sdk.readthedocs.io/zh-CN/latest/README.html)。 64 | 65 | ### 在线运行示例代码 66 | 67 | 文档中提供的示例代码可以通过[mybinder](https://mybinder.org/)在线运行。 您可以依次点击代码示例页顶部的 `🚀` -> `Binder` 来启动环境。环境中已配置好运行代码的必备依赖,因此相关代码可以直接运行。 68 | -------------------------------------------------------------------------------- /docs/zh-CN/_config.yml: -------------------------------------------------------------------------------- 1 | # Book settings 2 | # Learn more at https://jupyterbook.org/customize/config.html 3 | # Comprehensive example: https://github.com/executablebooks/jupyter-book/blob/master/docs/_config.yml 4 | 5 | title: python-conflux-sdk 6 | author: Conflux 7 | logo: ../logo.png 8 | 9 | # Force re-execution of notebooks on each build. 10 | # See https://jupyterbook.org/content/execute.html 11 | execute: 12 | execute_notebooks: 'off' 13 | 14 | # Information about where the book exists on the web 15 | repository: 16 | url: https://github.com/conflux-chain/python-conflux-sdk # Online location of your book 17 | path_to_book: docs/zh-CN # Optional path to your book, relative to the repository root 18 | branch: dev # Which branch of the repository should be used when creating links (optional) 19 | 20 | # Add GitHub buttons to your book 21 | # See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository 22 | html: 23 | use_issues_button: true 24 | use_repository_button: true 25 | 26 | launch_buttons: 27 | binderhub_url: "https://mybinder.org" 28 | colab_url: "https://colab.research.google.com" 29 | thebe: true 30 | 31 | sphinx: 32 | extra_extensions: 33 | - 'sphinx.ext.autodoc' 34 | - 'sphinx.ext.viewcode' 35 | - 'sphinx.ext.napoleon' 36 | - 'sphinx.ext.doctest' 37 | - 'IPython.sphinxext.ipython_console_highlighting' 38 | - 'sphinx_autodoc_typehints' 39 | # - 'sphinx.ext.intersphinx' 40 | # - 'sphinx.ext.autosummary' 41 | config: 42 | # autosummary_generate: True 43 | autoclass_content: "both" 44 | # autodoc_typehints: 'description' 45 | copyright: "2025" 46 | # autodoc_type_aliases: { 47 | # 'TxParam': 'cfx_utils.types.TxParam' 48 | # } 49 | typehints_fully_qualified: true 50 | # napoleon_use_param: true 51 | # always_document_param_types: true 52 | # napoleon_preprocess_types: true 53 | typehints_use_signature: true 54 | typehints_use_signature_return: true 55 | always_use_bars_union: true 56 | -------------------------------------------------------------------------------- /docs/zh-CN/_toc.yml: -------------------------------------------------------------------------------- 1 | ../en/_toc.yml -------------------------------------------------------------------------------- /docs/zh-CN/conf.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Auto-generated by `jupyter-book config` 3 | # If you wish to continue using _config.yml, make edits to that file and 4 | # re-generate this one. 5 | ############################################################################### 6 | always_use_bars_union = True 7 | author = 'Conflux' 8 | autoclass_content = 'both' 9 | comments_config = {'hypothesis': False, 'utterances': False} 10 | copyright = '2025' 11 | exclude_patterns = ['**.ipynb_checkpoints', '.DS_Store', 'Thumbs.db', '_build'] 12 | extensions = ['sphinx_togglebutton', 'sphinx_copybutton', 'myst_nb', 'jupyter_book', 'sphinx_thebe', 'sphinx_comments', 'sphinx_external_toc', 'sphinx.ext.intersphinx', 'sphinx_design', 'sphinx_book_theme', 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'sphinx.ext.doctest', 'IPython.sphinxext.ipython_console_highlighting', 'sphinx_autodoc_typehints', 'sphinx_jupyterbook_latex', 'sphinx_multitoc_numbering'] 13 | external_toc_exclude_missing = False 14 | external_toc_path = '_toc.yml' 15 | html_baseurl = '' 16 | html_favicon = '' 17 | html_logo = '../logo.png' 18 | html_sourcelink_suffix = '' 19 | html_theme = 'sphinx_book_theme' 20 | html_theme_options = {'search_bar_text': 'Search this book...', 'launch_buttons': {'notebook_interface': 'classic', 'binderhub_url': '', 'jupyterhub_url': '', 'thebe': False, 'colab_url': '', 'deepnote_url': ''}, 'path_to_docs': 'docs/zh-CN', 'repository_url': 'https://github.com/conflux-chain/python-conflux-sdk', 'repository_branch': 'dev', 'extra_footer': '', 'home_page_in_toc': True, 'announcement': '', 'analytics': {'google_analytics_id': '', 'plausible_analytics_domain': '', 'plausible_analytics_url': 'https://plausible.io/js/script.js'}, 'use_repository_button': True, 'use_edit_page_button': False, 'use_issues_button': True} 21 | html_title = 'python-conflux-sdk' 22 | latex_engine = 'pdflatex' 23 | myst_enable_extensions = ['colon_fence', 'dollarmath', 'linkify', 'substitution', 'tasklist'] 24 | myst_url_schemes = ['mailto', 'http', 'https'] 25 | nb_execution_allow_errors = False 26 | nb_execution_cache_path = '' 27 | nb_execution_excludepatterns = [] 28 | nb_execution_in_temp = False 29 | nb_execution_mode = 'off' 30 | nb_execution_timeout = 30 31 | nb_output_stderr = 'show' 32 | numfig = True 33 | pygments_style = 'sphinx' 34 | suppress_warnings = ['myst.domains'] 35 | typehints_fully_qualified = True 36 | typehints_use_signature = True 37 | typehints_use_signature_return = True 38 | use_jupyterbook_latex = True 39 | use_multitoc_numbering = True 40 | -------------------------------------------------------------------------------- /docs/zh-CN/examples/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/docs/zh-CN/examples/.gitkeep -------------------------------------------------------------------------------- /docs/zh-CN/examples/03-trace_transaction_status.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# \u8ffd\u8e2a\u4ea4\u6613\u72b6\u6001\n", 8 | "\n", 9 | "\u70b9\u51fb\u9876\u90e8\u680f\u7684 `\ud83d\ude80` -> `Binder` \u5728\u7ebf\u8fd0\u884c\u6b64\u793a\u4f8b\uff01" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## \u51c6\u5907\u5de5\u4f5c" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "# preparation: init w3 instance and prepare transaction hash object tx_hash\n", 26 | "from conflux_web3 import Web3\n", 27 | "\n", 28 | "w3 = Web3(Web3.HTTPProvider(\"https://test.confluxrpc.com\"))\n", 29 | "acct = w3.account.create()\n", 30 | "w3.cfx.default_account = acct\n", 31 | "faucet = w3.cfx.contract(name=\"Faucet\")\n", 32 | "\n", 33 | "# get a tx_hash\n", 34 | "tx_hash = faucet.functions.claimCfx().transact()" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "## Conflux \u4e2d\u7684\u4ea4\u6613\u72b6\u6001\n", 42 | "\n", 43 | "\u5728 Conflux \u4e2d\uff0c\u4ea4\u6613\u53d1\u9001\u540e\u4f1a\u7ecf\u5386\u4ee5\u4e0b\u51e0\u4e2a\u9636\u6bb5\uff1a\n", 44 | "\n", 45 | "1. `pending`\uff1a\u4ea4\u6613\u5df2\u53d1\u9001\uff0c\u4f46\u5c1a\u672a\u88ab\u6253\u5305\u8fdb\u533a\u5757\n", 46 | "2. `mined`\uff1a\u4ea4\u6613\u5df2\u88ab\u6253\u5305\u8fdb\u533a\u5757\uff0c\u4f46\u53ef\u80fd\u5c1a\u672a\u6267\u884c\n", 47 | "3. `executed`\uff1a\u4ea4\u6613\u5df2\u6267\u884c\u5b8c\u6210\n", 48 | "4. `confirmed`\uff1a\u4ea4\u6613\u5df2\u7ecf\u901a\u8fc7 PoW \u94fe\u7684\u786e\u8ba4\u89c4\u5219\uff0c\u8fd9\u610f\u5473\u7740\u9664\u975e PoW \u94fe\u906d\u53d7\u653b\u51fb\uff0c\u5426\u5219\u4ea4\u6613\u51e0\u4e4e\u4e0d\u53ef\u80fd\u88ab\u56de\u6eda\n", 49 | "5. `finalized`\uff1a\u4ea4\u6613\u5df2\u88ab PoS \u94fe**\u6700\u7ec8\u786e\u8ba4**\uff0c\u8fd9\u610f\u5473\u7740\u4ea4\u6613\u4e0d\u53ef\u80fd\u88ab\u56de\u6eda\uff0c\u4f46\u901a\u5e38\u9700\u8981 4~6 \u5206\u949f\u624d\u80fd\u8fbe\u5230\u8fd9\u4e2a\u72b6\u6001" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## \u8ffd\u8e2a\u4ea4\u6613\u72b6\u6001\n", 57 | "\n", 58 | "\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 `w3.cfx.wait_till_transaction_mined`\u3001`w3.cfx.wait_till_transaction_executed`\u3001`w3.cfx.wait_till_transaction_confirmed`\u3001`w3.cfx.wait_till_transaction_finalized` \u7b49\u65b9\u6cd5\u6765\u7b49\u5f85\u4ea4\u6613\u8fbe\u5230\u7279\u5b9a\u72b6\u6001\u3002" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 2, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "w3.cfx.wait_till_transaction_mined(tx_hash)\n", 68 | "w3.cfx.wait_till_transaction_executed(tx_hash)\n", 69 | "w3.cfx.wait_till_transaction_confirmed(tx_hash)\n", 70 | "if False: # 4\uff5e6 minutes is needed\n", 71 | " w3.cfx.wait_till_transaction_finalized(tx_hash)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "### \u8bed\u6cd5\u7cd6\n", 79 | "\n", 80 | "\u6211\u4eec\u7ecf\u5e38\u9700\u8981\u8ffd\u8e2a\u5df2\u53d1\u9001\u4ea4\u6613\u7684\u72b6\u6001\uff0c\u56e0\u6b64 SDK \u5bf9 `send_transaction` \u6216 `send_raw_transaction` \u8fd4\u56de\u7684\u5341\u516d\u8fdb\u5236\u4ea4\u6613\u54c8\u5e0c\u8fdb\u884c\u4e86\u5c01\u88c5\uff0c\u4f7f\u5176\u80fd\u591f\u65b9\u4fbf\u5730\u8bbf\u95ee\u4e0a\u8ff0 API\u3002" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 3, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "tx_hash.mined()\n", 90 | "tx_hash.executed()\n", 91 | "tx_hash.confirmed()\n", 92 | "if False:\n", 93 | " tx_hash.finalized()" 94 | ] 95 | } 96 | ], 97 | "metadata": { 98 | "kernelspec": { 99 | "display_name": "Python 3.9.13 ('sdk')", 100 | "language": "python", 101 | "name": "python3" 102 | }, 103 | "language_info": { 104 | "codemirror_mode": { 105 | "name": "ipython", 106 | "version": 3 107 | }, 108 | "file_extension": ".py", 109 | "mimetype": "text/x-python", 110 | "name": "python", 111 | "nbconvert_exporter": "python", 112 | "pygments_lexer": "ipython3", 113 | "version": "3.9.13" 114 | }, 115 | "orig_nbformat": 4, 116 | "vscode": { 117 | "interpreter": { 118 | "hash": "a3057bbf184bca29acc6a0538c16f383babaf4f383bee8205e8e826137244d0e" 119 | } 120 | } 121 | }, 122 | "nbformat": 4, 123 | "nbformat_minor": 2 124 | } -------------------------------------------------------------------------------- /docs/zh-CN/examples/11-construct_transaction_from_scratch.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# \u4ece\u96f6\u6784\u5efa\u4ea4\u6613\n", 9 | "\n", 10 | "\u70b9\u51fb\u9876\u90e8\u680f\u7684 `\ud83d\ude80` -> `Binder` \u5728\u7ebf\u8fd0\u884c\u6b64\u793a\u4f8b\uff01" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "from conflux_web3 import Web3\n", 20 | "\n", 21 | "w3 = Web3(Web3.HTTPProvider(\"https://test.confluxrpc.com\"))\n", 22 | "account = w3.account.from_key(\"0x....\") # fill your secret key here" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "\u6bcf\u4e2a\u5b57\u6bb5\u7684\u542b\u4e49\u5728[\u8fd9\u91cc](https://doc.confluxnetwork.org/docs/core/core-space-basics/transactions/tx-fields)\u6709\u8be6\u7ec6\u8bf4\u660e\u3002\u4e0b\u9762\u5c55\u793a\u5982\u4f55\u9009\u62e9\u6bcf\u4e2a\u5b57\u6bb5\u7684\u503c\u3002" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "prebuilt_tx = {\n", 39 | " 'from': account.address,\n", 40 | " 'nonce': w3.cfx.get_next_nonce(account.address),\n", 41 | " 'to': w3.account.create().address,\n", 42 | " 'value': 100,\n", 43 | " 'gasPrice': w3.cfx.gas_price,\n", 44 | " 'chainId': w3.cfx.chain_id,\n", 45 | " # 'gas': 21000, \n", 46 | " # 'storageLimit': 0,\n", 47 | " 'epochHeight': w3.cfx.epoch_number\n", 48 | "}\n", 49 | "\n", 50 | "# estimate\n", 51 | "estimate_result = w3.cfx.estimate_gas_and_collateral(prebuilt_tx)\n", 52 | "\n", 53 | "prebuilt_tx['gas'] = estimate_result['gasLimit']\n", 54 | "prebuilt_tx['storageLimit'] = estimate_result['storageCollateralized']\n", 55 | "\n", 56 | "w3.cfx.send_raw_transaction(\n", 57 | " account.sign_transaction(prebuilt_tx).raw_transaction \n", 58 | ").executed()" 59 | ] 60 | } 61 | ], 62 | "metadata": { 63 | "kernelspec": { 64 | "display_name": "Python 3.9.13 ('sdk')", 65 | "language": "python", 66 | "name": "python3" 67 | }, 68 | "language_info": { 69 | "codemirror_mode": { 70 | "name": "ipython", 71 | "version": 3 72 | }, 73 | "file_extension": ".py", 74 | "mimetype": "text/x-python", 75 | "name": "python", 76 | "nbconvert_exporter": "python", 77 | "pygments_lexer": "ipython3", 78 | "version": "3.12.7" 79 | }, 80 | "orig_nbformat": 4, 81 | "vscode": { 82 | "interpreter": { 83 | "hash": "a3057bbf184bca29acc6a0538c16f383babaf4f383bee8205e8e826137244d0e" 84 | } 85 | } 86 | }, 87 | "nbformat": 4, 88 | "nbformat_minor": 2 89 | } -------------------------------------------------------------------------------- /mmg.yml: -------------------------------------------------------------------------------- 1 | convert_without_ask: true 2 | # If false (default), ask for confirmation before converting. 3 | # If true, it is same as `mmg ** --yes`.` 4 | 5 | verbose: 2 6 | # 0: quiet(default), 1: normal, 2: verbose 7 | 8 | # log_dir: "logs" 9 | # Log files will be placed in this folder. 10 | # If the folder does not exist, error will occur. 11 | # If comment out, log files will not be generated. 12 | 13 | # The `tags_as` can be "folder" or "suffix" 14 | # - "folder" is for the folder docs structure. 15 | # - "suffix" is for the suffix docs structure 16 | # Please refer to the following link for details. 17 | # https://ultrabug.github.io/mkdocs-static-i18n/getting-started/quick-start/ 18 | 19 | jobs: 20 | - name: "Doc Examples" # Job name (optional) 21 | tag_as: "folder" # "folder" or "suffix" (required) 22 | source: "docs/examples/base" # Source folder containing the files to convert (required) 23 | recursive: true # If true, recursively search for files in subfolders (default: true) 24 | output_dir: "docs/examples" # Generated files will be placed in this folder (required) 25 | 26 | # - name: "README.md" # Job name (optional) 27 | # tag_as: "suffix" # "folder" or "suffix" (required) 28 | # source: "README.base.md" # Source file to convert (required) 29 | # output_dir: "./" # Generated file will be placed in this folder (required) -------------------------------------------------------------------------------- /postBuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | pip install py-solc-x 4 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pyright] 2 | 3 | typeCheckingMode = "strict" 4 | exclude=["build/*"] 5 | venv="sdk-313" 6 | reportUnknownMemberType = "information" 7 | reportUnknownVariableType = "warning" 8 | reportUnknownArgumentType = "information" 9 | reportMissingTypeArgument = "information" 10 | reportUnusedImport = "information" 11 | reportUnnecessaryIsInstance = "information" 12 | reportIncompatibleMethodOverride = "information" 13 | reportIncompatibleVariableOverride = "information" 14 | reportMissingTypeStubs = "information" 15 | reportPrivateImportUsage = "information" 16 | reportPrivateUsage = "information" 17 | reportUntypedFunctionDecorator = "information" 18 | reportUnnecessaryComparison = "information" 19 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = -p no:pytest_ethereum 3 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import ( 4 | find_packages, 5 | setup, 6 | ) 7 | 8 | VERSION = "1.4.4" 9 | DESCRIPTION = 'Python SDK for Conflux network' 10 | with open('./README.md') as readme: 11 | long_description = readme.read() 12 | 13 | extras_require = { 14 | 'tester': [ 15 | "docker>=7.1.0,<8", 16 | "pytest>=8,<9", 17 | "pytest-xdist>=3,<4", 18 | "typing_extensions", 19 | "pytest-cov", 20 | "ipfshttpclient==0.8.0a2", 21 | "python-dotenv", 22 | "setuptools", 23 | "pydantic>=2.6.0,<3", 24 | "cfx-address>=1.2.0", 25 | "pytest-xdist", 26 | "pytest-order", 27 | # "py-geth>=3.8.0,<4", 28 | ], 29 | 'linter': [ 30 | "black>=22.1.0,<23.0", 31 | # "flake8==3.8.3", 32 | # "isort>=4.2.15,<4.3.5", 33 | # "mypy==0.910", 34 | # "types-setuptools>=57.4.4,<58", 35 | # "types-requests>=2.26.1,<3", 36 | # "types-protobuf==3.19.13", 37 | ], 38 | 'docs': [ 39 | # "mock", 40 | # "sphinx-better-theme>=0.1.4", 41 | # "click>=5.1", 42 | # "configparser==3.5.0", 43 | # "contextlib2>=0.5.4", 44 | # "py-geth>=3.8.0,<4", 45 | # "py-solc>=0.4.0", 46 | # "pytest>=6.2.5,<7", 47 | # "sphinx>=4.2.0,<5", 48 | "jupyter-book", 49 | "mmg==2.0.0", 50 | "sphinx-autodoc-typehints", 51 | # "sphinx_rtd_theme>=0.1.9", 52 | # "toposort>=1.4", 53 | # "towncrier==18.5.0", 54 | # "urllib3", 55 | "wheel" 56 | ], 57 | 'dev': [ 58 | "bumpversion", 59 | "twine", 60 | # "flaky>=3.7.0,<4", 61 | # "hypothesis>=3.31.2,<6", 62 | # "pytest>=6.2.5,<7", 63 | # "pytest-asyncio>=0.18.1,<0.19", 64 | # "pytest-mock>=1.10,<2", 65 | # "pytest-pythonpath>=0.3", 66 | # "pytest-watch>=4.2,<5", 67 | # "pytest-xdist>=1.29,<2", 68 | # "setuptools>=38.6.0", 69 | # "tox>=1.8.0", 70 | # "tqdm>4.32,<5", 71 | # "twine>=1.13,<2", 72 | # "pluggy==0.13.1", 73 | # "when-changed>=0.3.0,<0.4" 74 | ], 75 | "ipfs": [ 76 | "ipfshttpclient==0.8.0a2", 77 | ], 78 | } 79 | 80 | extras_require['dev'] = ( 81 | extras_require['tester'] 82 | + extras_require['linter'] 83 | + extras_require['docs'] 84 | # + extras_require["ipfs"] ipfs is included in tester 85 | + extras_require['dev'] 86 | ) 87 | 88 | 89 | # Setting up 90 | setup( 91 | # the name must match the folder name 'verysimplemodule' 92 | name="conflux-web3", 93 | version=VERSION, 94 | author="Conflux-Dev", 95 | author_email="wenda.zhang@confluxnetwork.org", 96 | description=DESCRIPTION, 97 | long_description_content_type="text/markdown", 98 | long_description=long_description, 99 | packages=find_packages(), 100 | package_data={"conflux_web3": ["contract/metadata/*.json", "py.typed"], 101 | "cns": ["py.typed"]}, 102 | url='https://github.com/conflux-chain/python-conflux-sdk', 103 | install_requires=[ 104 | "web3==7.11.1", 105 | "cfx-address>=1.2.4,<1.3.0", 106 | "cfx-account>=1.2.2,<1.3.0", 107 | "cfx-utils>=1.0.5,<1.2.0", 108 | "typing-extensions<=4.13.2", 109 | ], # add any additional packages that need to be installed 110 | # needs to be installed along with your package. Eg: 'caer' 111 | extras_require=extras_require, 112 | keywords=['python', 'conflux', 'blockchain'], 113 | classifiers=[ 114 | 'Programming Language :: Python :: 3', 115 | 'Programming Language :: Python :: 3.8', 116 | 'Programming Language :: Python :: 3.9', 117 | 'Programming Language :: Python :: 3.10', 118 | 'Programming Language :: Python :: 3.11', 119 | 'Programming Language :: Python :: 3.12', 120 | 'Programming Language :: Python :: 3.13', 121 | "Operating System :: MacOS :: MacOS X", 122 | "Operating System :: Microsoft :: Windows", 123 | ] 124 | ) 125 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/_test_helpers/ENV_SETTING.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | TAG = "2.4.0" 5 | TESTNET_TAG = "2.3.0-3-testnet" 6 | REPO_NAME = "confluxchain/conflux-rust" 7 | DEV_IMAGE_FULL_NAME = f"{REPO_NAME}:{TAG}" 8 | TESTNET_IMAGE_FULL_NAME = f"{REPO_NAME}:{TESTNET_TAG}" 9 | LOCAL_NODE_NAME = "python-sdk-env" 10 | TESTNET_NODE_NAME = "python-sdk-env-testnet" 11 | LOCAL_HOST = "127.0.0.1" 12 | INTERNAL_PORT = 12537 13 | TESTNET_HOST_PORT = 12637 14 | 15 | TESTNET_CONFIG_DIR = os.path.join( 16 | os.path.dirname(__file__), 17 | "testnet" 18 | ) 19 | VOLUMES = { 20 | TESTNET_CONFIG_DIR: { 21 | "bind": "/root/run", 22 | "mode": "rw", 23 | } 24 | } 25 | 26 | HELPER_DIR = os.path.dirname(__file__) 27 | -------------------------------------------------------------------------------- /tests/_test_helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/tests/_test_helpers/__init__.py -------------------------------------------------------------------------------- /tests/_test_helpers/amb_metadata.json: -------------------------------------------------------------------------------- 1 | {"abi": [{"inputs": [{"internalType": "address", "name": "input", "type": "address"}, {"internalType": "bool", "name": "uselessFlag", "type": "bool"}], "name": "identity", "outputs": [{"internalType": "address", "name": "", "type": "address"}], "stateMutability": "nonpayable", "type": "function"}, {"inputs": [{"internalType": "string", "name": "input", "type": "string"}, {"internalType": "bool", "name": "uselessFlag", "type": "bool"}], "name": "identity", "outputs": [{"internalType": "string", "name": "", "type": "string"}], "stateMutability": "nonpayable", "type": "function"}], "bin": "608060405234801561001057600080fd5b5061033f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063193d35371461003b578063ac80da971461006b575b600080fd5b61005560048036038101906100509190610158565b61009c565b60405161006291906101a7565b60405180910390f35b61008560048036038101906100809190610227565b6100a7565b6040516100939291906102e5565b60405180910390f35b600082905092915050565b366000848491509150935093915050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100ed826100c2565b9050919050565b6100fd816100e2565b811461010857600080fd5b50565b60008135905061011a816100f4565b92915050565b60008115159050919050565b61013581610120565b811461014057600080fd5b50565b6000813590506101528161012c565b92915050565b6000806040838503121561016f5761016e6100b8565b5b600061017d8582860161010b565b925050602061018e85828601610143565b9150509250929050565b6101a1816100e2565b82525050565b60006020820190506101bc6000830184610198565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101e7576101e66101c2565b5b8235905067ffffffffffffffff811115610204576102036101c7565b5b6020830191508360018202830111156102205761021f6101cc565b5b9250929050565b6000806000604084860312156102405761023f6100b8565b5b600084013567ffffffffffffffff81111561025e5761025d6100bd565b5b61026a868287016101d1565b9350935050602061027d86828701610143565b9150509250925092565b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006102c48385610287565b93506102d1838584610298565b6102da836102a7565b840190509392505050565b600060208201905081810360008301526103008184866102b8565b9050939250505056fea2646970667358221220480e4741fca5aac3392af9606d9c111b1f1822a7701d1012d356d312b0ad477a64736f6c634300080d0033"} -------------------------------------------------------------------------------- /tests/_test_helpers/testnet/log.yaml: -------------------------------------------------------------------------------- 1 | refresh_rate: 30 seconds 2 | 3 | appenders: 4 | stdout: 5 | kind: console 6 | encoder: 7 | pattern: "{d} {h({l}):5.5} {T:<20.20} {t:12.12} - {m:10.20000}{n}" 8 | filters: 9 | - 10 | kind: "threshold" 11 | level: "info" 12 | 13 | 14 | logfile: 15 | kind: rolling_file 16 | path: "log/conflux.log" 17 | encoder: 18 | pattern: "{d} {h({l}):5.5} {T:<20.20} {t:12.12} - {m:.20000}{n}" 19 | 20 | policy: 21 | kind: compound 22 | trigger: 23 | kind: size 24 | limit: 2000 mb 25 | roller: 26 | kind: fixed_window 27 | pattern: "log/archive/conflux.{}.gz" 28 | count: 50 29 | 30 | root: 31 | level: info 32 | appenders: 33 | - stdout 34 | - logfile 35 | 36 | # We can set log level for crates individually 37 | loggers: 38 | network: 39 | level: info 40 | cfxcore: 41 | level: info 42 | rpc: 43 | level: info 44 | blockgen: 45 | level: info 46 | client: 47 | level: info 48 | cfx_storage: 49 | level: info 50 | cfx_statedb: 51 | level: info 52 | -------------------------------------------------------------------------------- /tests/_test_helpers/testnet/pos_config/genesis_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conflux-Chain/python-conflux-sdk/c5fd650e3c0d1484acad5e58371ee01b420f31e6/tests/_test_helpers/testnet/pos_config/genesis_file -------------------------------------------------------------------------------- /tests/_test_helpers/testnet/pos_config/initial_nodes.json: -------------------------------------------------------------------------------- 1 | {"initial_nodes":[{"address":"0x0000000000000000000000000000000000000000","bls_key":"88050f7a9e73c48bafa812370ca94f115ae4a708347e5d28d9750b60aa8b1f7a5039f04a6ad544c101f5a517b64bd286","vrf_key":"02f35c24a76883a9ca2ecc2c65192176582cd6715c933916c725d42bcca775bd2e","voting_power":341,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"87ad70ca07a86523c0e426d893cfd4b681cae65dfacdc01b8cc21d8f518dfb0768d8003a1087a186c9de87abab4765d3","vrf_key":"024ecd0644f085af37f5459355a1b3c28ba34e7aaa175b77000a61f5acc9a4b6c9","voting_power":1214,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"97b95f70b30048684ecfa3fec834c272fb872ca495eb434fe6bc5ebd183725efbe1b8ee935682f9f23dfd2a02c844e7b","vrf_key":"02b4a62c4b294fc0d0fe0361ac1d2464838f28d93c2b19771b5bd697cafe2203fc","voting_power":2000,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"a319bc0306f5d025610aada4f295859d162c8f40bc918c05f85ea65d454e98c0416e388de85ea742fa40b47f3e32dded","vrf_key":"024b940667411a387e4cf0f39b8349208aa68415a449febe1f21a6e8c2e28ba3d6","voting_power":10000,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"afccd4c5f9b9cd02807d7c513b0b8ca75ecc4a4553c1d74e437db45a40db720c5de791f28170eaad0867c3d724ed3a57","vrf_key":"0272d6bf3866fc642b3b2b5b40cc863322e8e035685a6eca32f7a8ee2afcf0e4bd","voting_power":501,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"b60bf9f209b9c00a503df9c1e04823ebf768b4cf2933c8983495013d40022b0155105b7bc5a4191b3783cb7f32b99505","vrf_key":"025dbda7c685716925a41cae66bb730246c5b960852e5cc2cdb58887aa2290fda3","voting_power":501,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"947aa54b808ad3a58465c43320894b4409e5e949794d5ce25b2b3570815947c338b631c62482b871ea640f0b8598e5d8","vrf_key":"036d9e75654d87fc5484d11d5265fb4e0910b1dc09d4e76a049a9c43c45891ba86","voting_power":501,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"893386c1d9fa338162662e5920e095c8b4221dffaab61eb6b754e92221f5e27172a099afce607c3b00df2b72e27c7082","vrf_key":"03df2ba33c52bf625acc8bf3331ce059617180da98d225fc9128258f13e5fffaf2","voting_power":13318,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}},{"address":"0x0000000000000000000000000000000000000000","bls_key":"adc3ffbb2ac81ddfc1a56829580b1f31c41e9cc90d82796f69918564c6febb6e266e80421e8f53af6b15cc884190963f","vrf_key":"03285d72aa04a01b7b9f2bc4d2201c6c9d542c01ace24370142b762550a9ce07b6","voting_power":501,"register_tx":{"nonce":"0x0","gas_price":"0x0","gas":"0x0","action":"Create","value":"0x0","storage_limit":0,"epoch_height":0,"chain_id":0,"data":[]}}],"initial_committee":[["3c64d606c0f3d35945572c9bfeb6c070f9b2b51f78ab4ecc32f1a48a63116303",4],["4c5c051b8f72b838e2682302fabde1a3d5372d39476674096110448ea22a6281",15],["8adcc328fbf598dcbe6d7c715da0768fbb098073abfb942e815d7bf95abb0327",2],["9a6fcdd8646a4c56735414187ae6b9f343732c2bb52f678f4fa2f92f3625402a",6],["9b63065c12300b32308ca18022178c1032544e07ffe2821c948c2d31fc5e200a",6],["9f9d23b2c233bbcf127d9bb01065e96a3db6a1ff07349206a859a8ca822f248b",18],["a41ee1a2ddbb7e94236781cbb827fc1d130c8a79ccc7f2eb00e2096f7420c81d",149],["ce257f9b12d67089057d71fe4f20f25569268582e1fa6f6673bb60934582f9f3",2],["e78ffdec03c1fb0bb36427b51b1d92c7a0aac663688eb20ea13162c188482b5c",98]],"initial_seed":"0x36a6a72ff2c1facd7932649f5358730957a52b39920e1ee07e4825f0f2ccbe08"} -------------------------------------------------------------------------------- /tests/_test_helpers/testnet/pos_config/pos_config.yaml: -------------------------------------------------------------------------------- 1 | 2 | base: 3 | #data_dir: ./pos_db 4 | role: validator 5 | waypoint: 6 | from_config: 0:81bf85cc19ca409846c142497552c998812aa96a72cdeab691892674db3e83cd 7 | consensus: 8 | round_initial_timeout_ms: 60000 9 | safety_rules: 10 | service: 11 | type: local 12 | execution: 13 | genesis_file_location: ./genesis_file 14 | logger: 15 | file: ./log/pos.log 16 | level: INFO 17 | #storage: 18 | #dir: ./pos_db/db 19 | -------------------------------------------------------------------------------- /tests/_test_helpers/testnet/throttling.toml: -------------------------------------------------------------------------------- 1 | # Throttling value format: 2 | # 3 | # Token bucket: ,,,, 4 | # max_tokens: maximum number of tokens in bucket. 5 | # init_tokens: number of tokens initialized in bucket. 6 | # recharge_rate: number of tokens recharged per second. 7 | # default_cost: number of tokens acquired at a time by default. 8 | # max_throttled_tolerates: maximum acquires allowed even throttled, otherwise client may be disconnected. 9 | 10 | [sync_protocol] 11 | Status="90,90,1,30,0" # heartbeat interval 30s 12 | NewBlockHashes="200,200,20,1,100" 13 | Transactions="50,50,10,1,50" 14 | GetBlockHeaders="200,200,100,1,100" 15 | NewBlock="20,20,4,1,20" 16 | GetBlocks="200,200,100,1,50" 17 | GetCompactBlocks="200,200,20,1,100" 18 | GetBlockTxn="200,200,20,1,100" 19 | DynamicCapabilityChange="20,20,5,1,30" 20 | TransactionDigests="50,50,10,1,50" 21 | GetTransactions="50,50,10,1,50" 22 | GetTransactionsFromTxHashes="50,50,10,1,50" 23 | GetBlockHashesByEpoch="50,50,10,1,50" 24 | SnapshotManifestRequest="50,50,10,1,50" 25 | SnapshotChunkRequest="50,50,10,1,50" 26 | Throttled="100,100,10,1,50" 27 | 28 | # Suggest to limit the IP address to access the RPC as well. 29 | [rpc] 30 | cfx_gasPrice="20,20,5,1,5" 31 | cfx_epochNumber="20,20,5,1,5" 32 | cfx_getBalance="50,50,10,1,5" 33 | cfx_getCode="20,20,5,1,5" 34 | cfx_getBlockByHash="20,20,5,1,5" 35 | cfx_getBlockByHashWithPivotAssumption="20,20,5,1,5" 36 | cfx_getBlockByEpochNumber="20,20,5,1,5" 37 | cfx_getBestBlockHash="50,50,10,1,5" 38 | cfx_getNextNonce="50,50,10,1,5" 39 | cfx_sendRawTransaction="50,50,10,1,5" 40 | cfx_call="20,20,5,1,5" 41 | cfx_getLogs="20,20,5,1,5" 42 | cfx_getTransactionByHash="50,50,10,1,5" 43 | cfx_estimateGas="10,10,2,1,1" 44 | cfx_getBlocksByEpoch="20,20,5,1,5" 45 | cfx_getTransactionReceipt="50,50,10,1,5" 46 | 47 | [rpc_local] 48 | 49 | [light_protocol] -------------------------------------------------------------------------------- /tests/base_features/test_api.py: -------------------------------------------------------------------------------- 1 | def test_api_version(w3): 2 | expected_version = "1.4.4" 3 | if "beta" not in expected_version: 4 | assert w3.api.startswith(expected_version) 5 | -------------------------------------------------------------------------------- /tests/base_features/test_default_account.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | 3 | def test_default_account_set(w3: Web3, secret_key): 4 | local_account = w3.account.from_key(secret_key) 5 | w3.cfx.default_account = local_account.address 6 | assert w3.cfx.default_account == local_account.address 7 | 8 | w3.cfx.default_account = local_account 9 | assert w3.cfx.default_account == local_account.address 10 | 11 | assert local_account.address in w3.wallet 12 | 13 | def test_web3_connection(w3: Web3): 14 | assert w3.is_connected() 15 | 16 | # a not valid Provider 17 | w3_ = Web3(Web3.HTTPProvider("http://127.0.0.1:11111")) 18 | assert not w3_.is_connected() 19 | -------------------------------------------------------------------------------- /tests/cns/conftest.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | import pytest 3 | import os 4 | from conflux_web3 import Web3 5 | from cfx_account import LocalAccount 6 | 7 | @pytest.fixture(scope="module") 8 | def ens_name() -> Union[str, None]: 9 | ens_name = os.environ.get("ENS_ACCOUNT_NAME", None) 10 | return ens_name 11 | 12 | @pytest.fixture(scope="module") 13 | def ens_account(moduled_w3: Web3) -> Union[LocalAccount, None]: 14 | secret = os.environ.get("ENS_ACCOUNT_SECRET", None) 15 | if not secret: 16 | return None 17 | return moduled_w3.account.from_key(secret) 18 | -------------------------------------------------------------------------------- /tests/cns/test_cns.py: -------------------------------------------------------------------------------- 1 | from uuid import uuid4 2 | import sys 3 | import pytest 4 | from conflux_web3 import Web3 5 | from cns import CNS 6 | from cfx_account import LocalAccount 7 | from conflux_web3.exceptions import ( 8 | NameServiceNotSet 9 | ) 10 | 11 | @pytest.fixture 12 | def to_test_cns_write_api(use_testnet: bool) -> bool: 13 | # currently we only support cns write api on testnet 14 | # so we only do tests on python3.8 to avoid nonce problems 15 | # return sys.version_info.minor == 8 and use_testnet 16 | return False 17 | 18 | def test_cns(w3: Web3, use_testnet: bool, ens_name: str, ens_account: LocalAccount): 19 | if use_testnet and ens_name: 20 | ns = CNS.fromWeb3(w3) 21 | assert ns.address(ens_name) 22 | if ens_account: 23 | assert ns.address(ens_name) == ens_account.address 24 | 25 | def test_cns_from_address(use_testnet: bool, ens_name: str, ens_account: LocalAccount): 26 | if use_testnet: 27 | provider = Web3.HTTPProvider("https://test.confluxrpc.com") 28 | cns = CNS(provider, "cfxtest:acemru7fu1u8brtyn3hrtae17kbcd4pd9u2m761bta") 29 | assert cns.address(ens_name) == ens_account.address 30 | w3 = Web3(provider, cns=cns) 31 | assert w3.cns.address(ens_name) == ens_account.address 32 | 33 | 34 | 35 | def test_cns_with_rpc(w3: Web3, use_testnet: bool, ens_name: str): 36 | if use_testnet: 37 | balance = w3.cfx.get_balance(ens_name) 38 | assert balance >= 0 39 | else: 40 | with pytest.raises(NameServiceNotSet): 41 | balance = w3.cfx.get_balance("hello45678oiuytrrtyuiytredcv.web3") 42 | 43 | 44 | def test_cns_usage_as_contract_param(w3: Web3, to_test_cns_write_api: bool, account: LocalAccount, ens_name: str): 45 | if to_test_cns_write_api: 46 | w3.cfx.default_account = account 47 | erc20 = w3.cfx.contract(name="ERC20", with_deployment_info=False) 48 | addr = erc20.constructor("Token", "T", 100).transact().executed()["contractCreated"] 49 | assert addr is not None 50 | erc20 = erc20(address=addr) 51 | assert erc20.functions.transfer(ens_name, 100).transact().executed() 52 | assert erc20.caller.balanceOf(ens_name) == 100 53 | 54 | 55 | def test_cns_as_sender(w3: Web3, to_test_cns_write_api: bool, ens_account: LocalAccount, ens_name: bool): 56 | if to_test_cns_write_api: 57 | w3.wallet.add_account(ens_account) 58 | w3.cfx.send_transaction({ 59 | "to": w3.account.create().address, 60 | "value": 100, 61 | "from": ens_name 62 | }).executed() 63 | 64 | 65 | def test_cns_as_contract_address(w3: Web3, to_test_cns_write_api: bool): 66 | if to_test_cns_write_api: 67 | faucet = w3.cfx.contract("faucet.web3", name="Faucet", with_deployment_info=False) 68 | assert faucet.address == "cfxtest:acejjfa80vj06j2jgtz9pngkv423fhkuxj786kjr61" 69 | account = w3.cfx.account.create() 70 | w3.cfx.default_account = account 71 | faucet.functions.claimCfx().transact().executed() 72 | 73 | def test_cns_as_default_account(w3: Web3, use_testnet: bool, ens_name: str, ens_account: LocalAccount): 74 | if use_testnet: 75 | w3.cfx.default_account = ens_name 76 | assert w3.cfx.default_account == ens_account.address 77 | 78 | def test_cns_owner(w3: Web3, use_testnet: bool, ens_name: str): 79 | if use_testnet: 80 | assert w3.ens.owner(ens_name) 81 | 82 | # def test_setup_owner(w3: Web3, account): 83 | # w3.cns.allow_unstable_api = True 84 | # w3.cfx.default_account = account 85 | # w3.cns.setup_owner("test.web3", wrapped=True) 86 | 87 | 88 | def test_setup_address(w3: Web3, to_test_cns_write_api: bool, ens_account: LocalAccount): 89 | if to_test_cns_write_api: 90 | w3.cns.allow_unstable_api = True 91 | w3.cfx.default_account = ens_account 92 | random_address = w3.account.create().address 93 | 94 | assert w3.cns.setup_address("test.web3", random_address, wrapped=True).executed() 95 | 96 | # test setup subdomain address 97 | random_subdomain = uuid4() 98 | assert w3.cns.setup_address(f"{random_subdomain}.test.web3", random_address, wrapped=True).executed() 99 | assert w3.cns.address(f"{random_subdomain}.test.web3") == random_address 100 | 101 | 102 | def test_cns_wallet(w3: Web3, use_testnet: bool): 103 | if use_testnet: 104 | assert w3.wallet is w3.cns.w3.wallet 105 | 106 | 107 | def test_cns_default_account(w3: Web3, use_testnet: bool, account: LocalAccount): 108 | if use_testnet: 109 | w3.cfx.default_account = account 110 | assert w3.cfx.default_account == w3.ens.w3.cfx.default_account 111 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable, Sequence, Union 2 | import os 3 | import pytest 4 | import dotenv 5 | from tests._test_helpers.node import ( 6 | LocalNode, BaseNode, RemoteTestnetNode, LocalTestnetNode 7 | ) 8 | 9 | from cfx_account.account import LocalAccount 10 | from cfx_account import Account 11 | from conflux_web3 import ( 12 | Web3 13 | ) 14 | from conflux_web3.types import ( 15 | Base32Address 16 | ) 17 | 18 | dotenv.load_dotenv() 19 | 20 | @pytest.fixture(scope="session") 21 | def use_testnet() -> bool: 22 | return bool(os.environ.get("TESTNET_SECRET")) or bool(os.environ.get("USE_TESTNET")) 23 | 24 | @pytest.fixture(scope="session") 25 | def worker_index(worker_id: str) -> int: 26 | if worker_id == "master": 27 | return 0 28 | return int(worker_id[2:]) 29 | 30 | @pytest.fixture(scope="session") 31 | def node(use_testnet: bool, worker_index: int) -> Iterable[BaseNode]: 32 | if use_testnet: 33 | node = RemoteTestnetNode() # connection error might occur if using public RPC 34 | # node = LocalTestnetNode() 35 | yield node 36 | # node.exit() 37 | else: 38 | node = LocalNode(node_name=f"sdk-test-{worker_index}", index=worker_index) 39 | yield node 40 | # node.exit() 41 | 42 | @pytest.fixture(scope="session") 43 | def node_url(node: BaseNode) -> str: 44 | return node.url 45 | 46 | @pytest.fixture(scope="session") 47 | def secret_key(node: LocalNode, worker_index: int, use_testnet: bool) -> Union[str, None]: 48 | """ 49 | Returns: 50 | str: secret key with enough balance 51 | """ 52 | if not use_testnet: 53 | return node.secrets[0] 54 | try: 55 | return node.secrets[worker_index] 56 | except IndexError: 57 | w3 = Web3(Web3.HTTPProvider(node.url)) 58 | acct = w3.cfx.account.create() 59 | w3.wallet.add_account(acct) 60 | faucet = w3.cfx.contract(name="Faucet") 61 | faucet.functions.claimCfx().transact({ 62 | "from": acct.address, 63 | }).executed() 64 | return f"0x{acct.key.hex()}" 65 | 66 | # no scope here 67 | @pytest.fixture 68 | def w3(node_url: str) -> Web3: 69 | """ 70 | Returns: 71 | Web3: a web3 instance 72 | """ 73 | provider = Web3.HTTPProvider(node_url) 74 | w3 = Web3(provider=provider) 75 | return w3 76 | 77 | @pytest.fixture(scope="session") 78 | def account(node_url: str, secret_key: str) -> LocalAccount: 79 | """external_account, not supported by node 80 | """ 81 | provider = Web3.HTTPProvider(node_url) 82 | w3 = Web3(provider=provider) 83 | acct = w3.account.from_key(secret_key) 84 | if w3.cfx.get_balance(acct.address) == 0: 85 | raise Exception("Unexpected exception: provided account has no balance") 86 | return w3.account.from_key(secret_key) 87 | 88 | @pytest.fixture(scope="module") 89 | def moduled_w3(node_url: str, account: LocalAccount) -> Web3: 90 | """ 91 | a web3 instance for the convenience to create module shared objects 92 | e.g. a transaction or a contract which is required for the module 93 | NOTE: DON'T CHANGE PROPERTY OF THIS W3 94 | """ 95 | provider = Web3.HTTPProvider(node_url) 96 | w3 = Web3(provider=provider) 97 | w3.cfx.default_account = account 98 | return w3 99 | 100 | @pytest.fixture(scope="session") 101 | def address(account: LocalAccount) -> Base32Address: 102 | return account.base32_address 103 | 104 | @pytest.fixture 105 | def embedded_accounts(w3: Web3, use_testnet: bool) -> Sequence[Base32Address]: 106 | if use_testnet: 107 | return [] 108 | return w3.cfx.accounts 109 | -------------------------------------------------------------------------------- /tests/middleware/test_pending.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | from typing import TYPE_CHECKING 4 | from cfx_account.account import LocalAccount 5 | 6 | 7 | if TYPE_CHECKING: 8 | from conflux_web3 import Web3 9 | 10 | @pytest.mark.order(1) 11 | def test_pending(w3: "Web3", account: LocalAccount, use_testnet: bool): 12 | # activate by default 13 | # w3.middleware_onion.add(PendingTransactionMiddleware) 14 | addr = account.address 15 | w3.wallet.add_account(account) 16 | 17 | pending = w3.cfx.send_transaction({ 18 | "from": addr, 19 | "to": w3.cfx.account.create().address, 20 | "value": 100, 21 | }) 22 | pending.mined() 23 | pending.executed() 24 | pending.confirmed() 25 | if use_testnet and os.environ.get("TEST_FINALIZATION", None): 26 | with pytest.warns(UserWarning): 27 | pending.finalized() 28 | -------------------------------------------------------------------------------- /tests/providers/test_http_provider_retry.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from unittest.mock import ( 3 | patch, 4 | ) 5 | from conflux_web3 import Web3 6 | from conflux_web3._utils.rpc_abi import ( 7 | RPC 8 | ) 9 | from requests.exceptions import ( 10 | Timeout, 11 | ) 12 | from conflux_web3.providers.rpc import check_if_retry_on_failure 13 | 14 | @pytest.fixture 15 | def w3() -> Web3: 16 | return Web3(Web3.HTTPProvider()) 17 | 18 | 19 | def test_default_check_if_retry_on_failure(): 20 | assert check_if_retry_on_failure(RPC.cfx_sendRawTransaction) == False 21 | assert check_if_retry_on_failure(RPC.cfx_getBalance) == True 22 | 23 | 24 | def test_check_if_retry_on_failure_with_allowlist(): 25 | assert check_if_retry_on_failure(RPC.cfx_sendRawTransaction, allowlist=["cfx_sendRawTransaction"]) == True 26 | assert check_if_retry_on_failure(RPC.cfx_sendRawTransaction, allowlist=["cfx_sendTransaction"]) == False 27 | 28 | def test_check_if_retry_on_failure_with_disallowlist(): 29 | assert check_if_retry_on_failure(RPC.cfx_sendRawTransaction, disallowlist=["cfx_sendRawTransaction"]) == False 30 | assert check_if_retry_on_failure(RPC.cfx_sendRawTransaction, disallowlist=["cfx_sendTransaction"]) == True 31 | 32 | def test_default_exception_retry(w3: Web3): 33 | with patch( 34 | "web3.providers.rpc.rpc.HTTPSessionManager.make_post_request" 35 | ) as make_post_request_mock: 36 | make_post_request_mock.side_effect = Timeout 37 | 38 | with pytest.raises(Timeout): 39 | w3.cfx.get_status() 40 | assert make_post_request_mock.call_count == 5 # default retry count is 5 41 | 42 | def test_default_exception_no_retry(w3: Web3): 43 | with patch( 44 | "web3.providers.rpc.rpc.HTTPSessionManager.make_post_request" 45 | ) as make_post_request_mock: 46 | make_post_request_mock.side_effect = Timeout 47 | 48 | with pytest.raises(Timeout): 49 | w3.cfx.send_raw_transaction("0x") 50 | assert make_post_request_mock.call_count == 1 # only once -------------------------------------------------------------------------------- /tests/rpcs/cfx/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from conflux_web3 import Web3 3 | from conflux_web3.types import Base32Address, HexBytes 4 | from conflux_web3.contract.metadata import get_contract_metadata 5 | 6 | @pytest.fixture(scope="module") 7 | def tx_hash(moduled_w3: Web3, secret_key: str) -> HexBytes: 8 | w3 = moduled_w3 9 | status = w3.cfx.get_status() 10 | account = w3.account.from_key(secret_key) 11 | addr = account.address 12 | 13 | tx = { 14 | 'from': addr, 15 | 'nonce': w3.cfx.get_next_nonce(addr), 16 | 'gas': 21000, 17 | 'to': "cfxtest:aamd4myx7f3en2yu95xye7zb78gws09gj2ykmv9p58", 18 | 'value': 100, 19 | 'gasPrice': 10**9, 20 | 'chainId': w3.cfx.chain_id, 21 | 'storageLimit': 0, 22 | 'epochHeight': status['epochNumber'] 23 | } 24 | signed = account.sign_transaction(tx) 25 | rawTx = signed.raw_transaction 26 | h = w3.cfx.send_raw_transaction(rawTx) 27 | h.executed() 28 | return h 29 | 30 | @pytest.fixture(scope="module") 31 | def contract_address(moduled_w3: Web3): 32 | erc20_metadata = get_contract_metadata("ERC20") 33 | erc20 = moduled_w3.cfx.contract(bytecode=erc20_metadata["bytecode"], abi=erc20_metadata["abi"]) 34 | hash = erc20.constructor(name="Coin", symbol="C", initialSupply=10**18).transact() 35 | contract_address = hash.executed()["contractCreated"] 36 | return contract_address 37 | 38 | @pytest.fixture(scope="module") 39 | def tx_with_log(moduled_w3: Web3, contract_address: Base32Address) -> HexBytes: 40 | w3 = moduled_w3 41 | erc20_metadata = get_contract_metadata("ERC20") 42 | erc20 = w3.cfx.contract(address=contract_address, abi=erc20_metadata["abi"]) 43 | 44 | hash = erc20.functions.transfer(w3.account.create().address, 100).transact() 45 | hash.executed() 46 | return hash -------------------------------------------------------------------------------- /tests/rpcs/cfx/filter/test_block_filter.py: -------------------------------------------------------------------------------- 1 | import time 2 | from conflux_web3 import Web3 3 | from tests._test_helpers.type_check import TypeValidator 4 | 5 | class TestBlockFilter: 6 | def test_block_filter(self, moduled_w3: Web3): 7 | block_filter_id = moduled_w3.cfx.new_block_filter() 8 | time.sleep(5) 9 | new_blocks = moduled_w3.cfx.get_filter_changes(block_filter_id) 10 | assert len(new_blocks) > 0 11 | for block_hash in new_blocks: 12 | assert TypeValidator.isinstance(block_hash, bytes) 13 | assert len(block_hash) == 32 14 | assert moduled_w3.cfx.uninstall_filter(block_filter_id) -------------------------------------------------------------------------------- /tests/rpcs/cfx/filter/test_log_filter.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pytest 3 | from conflux_web3 import Web3 4 | from conflux_web3.contract import ConfluxContract 5 | from tests._test_helpers.type_check import TypeValidator 6 | 7 | class TestLogFilter: 8 | @pytest.fixture(scope="class") 9 | def contract(self, moduled_w3: Web3): 10 | w3 = moduled_w3 11 | contract_address = w3.cfx.contract(name="ERC20").constructor(name="Coin", symbol="C", initialSupply=10**18).transact().executed()["contractCreated"] 12 | assert contract_address is not None 13 | return w3.cfx.contract(contract_address, name="ERC20") 14 | 15 | 16 | def test_log_filter(self, moduled_w3: Web3, contract: ConfluxContract): 17 | log_filter_id = moduled_w3.cfx.new_filter(address = contract.address) 18 | contract.functions.transfer(contract.address, 1**18).transact().executed() 19 | time.sleep(1) 20 | logs = moduled_w3.cfx.get_filter_changes(log_filter_id) 21 | assert len(logs) > 0 22 | for log in logs: 23 | TypeValidator.validate_typed_dict(log, "LogReceipt") 24 | assert moduled_w3.cfx.uninstall_filter(log_filter_id) -------------------------------------------------------------------------------- /tests/rpcs/cfx/filter/test_pending_tx_filter.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from conflux_web3 import Web3 3 | from conflux_web3.types import Drip 4 | from tests._test_helpers.type_check import TypeValidator 5 | 6 | class TestPendingTxFilter: 7 | 8 | def test_pending_tx_filter(self, moduled_w3: Web3): 9 | pending_tx_filter_id = moduled_w3.cfx.new_pending_transaction_filter() 10 | constucted_pending_tx = moduled_w3.cfx.send_transaction({ 11 | "to": moduled_w3.address.zero_address(), 12 | "value": Drip(100), 13 | }) 14 | pending_txs = moduled_w3.cfx.get_filter_changes(pending_tx_filter_id) 15 | assert len(pending_txs) > 0 16 | for pending_tx in pending_txs: 17 | assert TypeValidator.isinstance(pending_tx, bytes) 18 | assert len(pending_tx) == 32 19 | 20 | constucted_pending_tx.executed() 21 | assert moduled_w3.cfx.uninstall_filter(pending_tx_filter_id) -------------------------------------------------------------------------------- /tests/rpcs/cfx/test_cfx_account_query_rpcs.py: -------------------------------------------------------------------------------- 1 | from hexbytes import HexBytes 2 | import pytest 3 | from conflux_web3 import Web3 4 | from conflux_web3.types import Base32Address, Drip 5 | from tests._test_helpers.type_check import TypeValidator 6 | 7 | class TestAccountQuery: 8 | 9 | def test_get_balance(self, w3: Web3, address: Base32Address): 10 | balance = w3.cfx.get_balance(address, w3.cfx.epoch_number-5) 11 | # the balance is supposed to be non-zero 12 | assert balance > 0 13 | assert isinstance(balance, Drip) 14 | 15 | # def test_get_balance_empty_param(self, w3: Web3, use_testnet): 16 | # # TODO: remove use_testnet if statement after testnet node is repaired 17 | # if use_testnet: 18 | # return 19 | # with pytest.raises(TypeError): 20 | # w3.cfx.get_balance() 21 | 22 | 23 | def test_get_staking_balance(self, w3: Web3, address: Base32Address): 24 | staking_balance = w3.cfx.get_staking_balance(address, w3.cfx.epoch_number-5) 25 | assert staking_balance >= 0 26 | assert isinstance(staking_balance, Drip) 27 | # TODO: use staking balance contract 28 | 29 | 30 | def test_get_code(self, w3: Web3, contract_address: Base32Address): 31 | # test different cases 32 | # contract address / user address 33 | contract_code = w3.cfx.get_code(contract_address, w3.cfx.epoch_number_by_tag("latest_state")) 34 | assert isinstance(contract_code, bytes) # reorg might happen, so we only assert the variable type 35 | 36 | user_code = w3.cfx.get_code(w3.cfx.account.create().address) 37 | assert user_code == HexBytes("0x") 38 | 39 | 40 | def test_get_admin(self, w3: Web3, contract_address: Base32Address): 41 | # test different cases 42 | # contract address / user address 43 | contract_code = w3.cfx.get_admin(contract_address, w3.cfx.epoch_number_by_tag("latest_state")) 44 | assert isinstance(contract_code, Base32Address) # reorg might happen, so we only assert the variable type 45 | 46 | random_contract_address = w3.address(w3.cfx.account.create().address.replace("0x8", "0x1")) 47 | user_admin = w3.cfx.get_admin(random_contract_address) 48 | assert user_admin is None 49 | 50 | 51 | def test_get_storage_at(self, w3: Web3, contract_address: Base32Address, use_testnet: bool): 52 | # TODO: a potential bug in RPC, at present we ignore the testing in local node 53 | if use_testnet: 54 | storage = w3.cfx.get_storage_at(contract_address, 100, w3.cfx.epoch_number_by_tag("latest_state")) 55 | assert isinstance(storage, bytes) 56 | else: 57 | pass 58 | 59 | 60 | def test_get_storage_root(self, w3: Web3, contract_address: Base32Address): 61 | root = w3.cfx.get_storage_root(contract_address, w3.cfx.epoch_number_by_tag("latest_state")) 62 | TypeValidator.validate_typed_dict(root, "StorageRoot") 63 | 64 | # TODO: check RPC work pattern 65 | # root = w3.cfx.get_storage_root(w3.account.create().address) 66 | # assert not root 67 | 68 | 69 | def test_get_collateral_for_storage(self, w3: Web3, address: Base32Address): 70 | storage = w3.cfx.get_collateral_for_storage(address, w3.cfx.epoch_number_by_tag("latest_state")) 71 | 72 | assert isinstance(storage, int) 73 | 74 | 75 | def test_get_sponsor_info(self, w3: Web3, contract_address: Base32Address): 76 | sponsor_info = w3.cfx.get_sponsor_info(contract_address, w3.cfx.epoch_number_by_tag("latest_state")) 77 | # assert sponsor_info 78 | TypeValidator.validate_typed_dict(sponsor_info, "SponsorInfo") 79 | 80 | 81 | def test_get_account(self, w3: Web3, address: Base32Address): 82 | account_info = w3.cfx.get_account(address, w3.cfx.epoch_number_by_tag("latest_state")) 83 | TypeValidator.validate_typed_dict(account_info, "AccountInfo") 84 | 85 | 86 | def test_get_deposit_list(self, w3:Web3, address: Base32Address): 87 | deposit_list = w3.cfx.get_deposit_list(address) 88 | for deposit_info in deposit_list: 89 | TypeValidator.validate_typed_dict(deposit_info, "DepositInfo") 90 | 91 | 92 | def test_get_vote_list(self, w3:Web3, address: Base32Address): 93 | vote_list = w3.cfx.get_vote_list(address, w3.cfx.epoch_number_by_tag("latest_state")) 94 | for vote_info in vote_list: 95 | TypeValidator.validate_typed_dict(vote_info, "VoteInfo") 96 | -------------------------------------------------------------------------------- /tests/rpcs/cfx/test_cfx_block_rpcs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hexbytes import HexBytes 3 | from conflux_web3 import Web3 4 | from conflux_web3.types import BlockData 5 | from tests._test_helpers.type_check import TypeValidator 6 | 7 | def preprocess_block_data(block_data: BlockData, use_testnet: bool) -> BlockData: 8 | """ 9 | preprocess block in local testnet 10 | """ 11 | if not use_testnet: 12 | # local node may not run pos chain 13 | block_data = dict(block_data) # type: ignore 14 | block_data['posReference'] = HexBytes("0x0") # type: ignore 15 | return block_data 16 | 17 | class TestBlock: 18 | @pytest.fixture 19 | def block_hash(self, w3: Web3, tx_hash: HexBytes): 20 | return w3.cfx.wait_for_transaction_receipt(tx_hash)['blockHash'] 21 | 22 | @pytest.fixture 23 | def block_data(self, w3:Web3, block_hash: bytes, use_testnet: bool): 24 | block_data = w3.cfx.get_block_by_hash(block_hash, True) 25 | assert block_data is not None 26 | block_data = preprocess_block_data(block_data, use_testnet) 27 | return block_data 28 | 29 | @pytest.fixture 30 | def no_full_block_data(self, w3:Web3, block_hash: bytes, use_testnet: bool): 31 | block_data = w3.cfx.get_block_by_hash(block_hash, False) 32 | assert block_data is not None 33 | block_data = preprocess_block_data(block_data, use_testnet) 34 | return block_data 35 | 36 | 37 | def test_get_block_by_hash(self, block_data: BlockData, no_full_block_data: BlockData): 38 | TypeValidator.validate_typed_dict(block_data, "BlockData") 39 | TypeValidator.validate_typed_dict(no_full_block_data, "BlockData") 40 | 41 | 42 | def test_get_block_by_epoch_number(self, w3:Web3, block_data: BlockData, use_testnet: bool): 43 | assert block_data['epochNumber'] is not None 44 | data_ = w3.cfx.get_block_by_epoch_number(block_data['epochNumber'], True) 45 | data_ = preprocess_block_data(data_, use_testnet) 46 | TypeValidator.validate_typed_dict(data_, "BlockData") 47 | 48 | 49 | def test_get_block_by_block_number(self, w3:Web3, block_data: BlockData, use_testnet: bool): 50 | assert block_data['blockNumber'] is not None 51 | data_ = w3.cfx.get_block_by_block_number(block_data['blockNumber'], True) 52 | data_ = preprocess_block_data(data_, use_testnet) 53 | assert dict(data_) == dict(block_data) 54 | 55 | 56 | def test_get_best_block_hash(self, w3:Web3): 57 | best_block_hash = w3.cfx.get_best_block_hash() 58 | assert isinstance(best_block_hash, HexBytes) 59 | 60 | 61 | def test_get_blocks_by_epoch(self, w3: Web3): 62 | blocks = w3.cfx.get_blocks_by_epoch("latest_state") 63 | for block_hash in blocks: 64 | assert isinstance(block_hash, bytes) 65 | 66 | 67 | def test_get_skipped_blocks(self, w3: Web3): 68 | blocks = w3.cfx.get_skipped_blocks_by_epoch("latest_state") 69 | for block_hash in blocks: 70 | assert isinstance(block_hash, bytes) 71 | 72 | def test_get_blocks_by_hash_with_pivot_assumptions(self, w3: Web3, use_testnet: bool): 73 | epoch_number = w3.cfx.epoch_number_by_tag("latest_confirmed") 74 | blocks = w3.cfx.get_blocks_by_epoch(epoch_number) 75 | block_data = w3.cfx.get_block_by_hash_with_pivot_assumptions( 76 | blocks[0], 77 | blocks[-1], 78 | epoch_number, 79 | ) 80 | block_data = preprocess_block_data(block_data, use_testnet) 81 | TypeValidator.validate_typed_dict(block_data, "BlockData") 82 | 83 | 84 | def test_get_block(self, w3: Web3, block_data: BlockData, use_testnet: bool): 85 | epoch_number = block_data["epochNumber"] 86 | assert epoch_number is not None 87 | hash = block_data["hash"] 88 | str_hash = hash.hex() 89 | for block_identifier in [epoch_number, str_hash, hash, "latest_state"]: 90 | block = w3.cfx.get_block(block_identifier) 91 | block = preprocess_block_data(block, use_testnet) 92 | TypeValidator.validate_typed_dict(block, "BlockData") 93 | 94 | 95 | def test_epoch_receipts(self, w3: Web3, block_data: BlockData): 96 | epoch_number = block_data["epochNumber"] 97 | assert epoch_number is not None 98 | epoch_receipts = w3.cfx.get_epoch_receipts(epoch_number, True) 99 | for block_receipts in epoch_receipts: 100 | for tx_receipt in block_receipts: 101 | TypeValidator.validate_typed_dict(tx_receipt, "TxReceiptWithSpace") 102 | -------------------------------------------------------------------------------- /tests/rpcs/cfx/test_cfx_nonce_rpcs.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | from conflux_web3.types import Base32Address 3 | 4 | class TestNonce: 5 | def test_get_next_nonce(self, w3: Web3, address: Base32Address): 6 | nonce = w3.cfx.get_next_nonce(address) 7 | assert nonce >= 0 8 | # if default account is set, 9 | # default account is used as address default param 10 | # w3.cfx.default_account = address 11 | # default_nonce = w3.cfx.get_next_nonce() 12 | # assert default_nonce == nonce 13 | 14 | def test_get_transaction_count(self, w3: Web3, address: Base32Address): 15 | nonce = w3.cfx.get_transaction_count(address) 16 | assert nonce >= 0 17 | 18 | # def test_get_next_nonce_empty_param(self, w3: Web3, use_testnet): 19 | # # TODO: remove use_testnet if statement after testnet node is repaired 20 | # if use_testnet: 21 | # return 22 | # with pytest.raises(ValueError): 23 | # w3.cfx.get_next_nonce() 24 | -------------------------------------------------------------------------------- /tests/rpcs/cfx/test_cfx_rpcs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hexbytes import HexBytes 3 | 4 | from cfx_address import Base32Address 5 | from conflux_web3 import Web3 6 | from conflux_web3.types import ( 7 | Drip, 8 | ) 9 | from conflux_web3.contract.metadata import get_contract_metadata 10 | from tests._test_helpers.type_check import TypeValidator 11 | 12 | # Note that we only test if SDK works as expected, especially for request and result formatting. 13 | # We don't test if RPC works as expected 14 | 15 | 16 | def test_get_tx(moduled_w3: Web3, contract_address: Base32Address): 17 | """test get_transaction(_by_hash) and get_transaction_receipt 18 | """ 19 | w3 = moduled_w3 20 | erc20_metadata = get_contract_metadata("ERC20") 21 | erc20 = w3.cfx.contract(address=contract_address, abi=erc20_metadata["abi"]) 22 | 23 | tx_hash = erc20.functions.transfer(w3.account.create().address, 100).transact() 24 | transaction_data = w3.cfx.get_transaction(tx_hash) 25 | # transaction not added to chain 26 | TypeValidator.validate_tx_data(transaction_data) 27 | transaction_receipt = w3.cfx.wait_for_transaction_receipt(tx_hash) 28 | # already added 29 | TypeValidator.validate_tx_data(transaction_data) 30 | 31 | TypeValidator.validate_typed_dict(transaction_receipt, "TxReceipt") 32 | 33 | def test_accounts(w3: Web3, use_testnet: bool): 34 | if use_testnet: 35 | assert True 36 | return 37 | 38 | local_node_accounts = w3.cfx.accounts 39 | assert len(local_node_accounts) == 10 40 | 41 | # def test_get_logs(w3: Web3): 42 | # """see test_contract 43 | # """ 44 | # pass 45 | 46 | 47 | def test_get_confirmation_risk(w3: Web3, tx_hash: HexBytes): 48 | blockHash = w3.cfx.wait_for_transaction_receipt(tx_hash)['blockHash'] 49 | risk = w3.cfx.get_confirmation_risk_by_hash(blockHash) 50 | assert risk < 1 51 | 52 | 53 | 54 | def test_fee_history(moduled_w3: Web3): 55 | w3 = moduled_w3 56 | fee_history = w3.cfx.fee_history(5, "latest_state", [20,50]) 57 | TypeValidator.validate_typed_dict(fee_history, "FeeHistory") 58 | 59 | 60 | def test_max_priority_fee(moduled_w3: Web3): 61 | w3 = moduled_w3 62 | fee = w3.cfx.max_priority_fee 63 | assert isinstance(fee, Drip) 64 | -------------------------------------------------------------------------------- /tests/rpcs/test_cfx_status_query_rpcs.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | from conflux_web3.types import Drip, GDrip 3 | from tests._test_helpers.type_check import TypeValidator 4 | 5 | class TestStatusQuery: 6 | def test_get_status(self, w3: Web3): 7 | status = w3.cfx.get_status() 8 | TypeValidator.validate_typed_dict(status, "NodeStatus") 9 | 10 | def test_chain_id(self, w3: Web3): 11 | assert w3.cfx.chain_id > 0 12 | 13 | def test_gas_price(self, w3: Web3): 14 | gas_price = w3.cfx.gas_price 15 | assert gas_price >= GDrip(1) 16 | assert isinstance(gas_price, Drip) 17 | 18 | def test_client_version(self, w3: Web3): 19 | assert w3.cfx.client_version 20 | 21 | def test_get_interest_rate(self, w3: Web3): 22 | interest_rate = w3.cfx.get_interest_rate(w3.cfx.epoch_number_by_tag("latest_state")) 23 | assert isinstance(interest_rate, int) 24 | 25 | def test_get_accumulate_interest_rate(self, w3: Web3): 26 | assert isinstance( 27 | w3.cfx.get_accumulate_interest_rate(w3.cfx.epoch_number_by_tag("latest_state")), 28 | int 29 | ) 30 | 31 | def test_get_block_reward_info(self, w3: Web3): 32 | info_sequence = w3.cfx.get_block_reward_info(w3.cfx.epoch_number_by_tag("latest_checkpoint")) 33 | for info in info_sequence: 34 | TypeValidator.validate_typed_dict(info, "BlockRewardInfo") 35 | 36 | def test_get_pos_economics(self, w3: Web3): 37 | info = w3.cfx.get_pos_economics(w3.cfx.epoch_number_by_tag("latest_state")) 38 | TypeValidator.validate_typed_dict(info, "PoSEconomicsInfo") 39 | 40 | # TODO: finish this test after pos RPC finished 41 | # def test_get_pos_reward_by_epoch(self, w3: Web3, use_testnet: bool): 42 | # if use_testnet: 43 | # info = w3.cfx.get_pos_reward_by_epoch("100") 44 | # TypeValidator.validate_typed_dict(info, "PoSEpochRewardInfo") 45 | 46 | def test_get_params_from_vote(self, w3: Web3): 47 | info = w3.cfx.get_params_from_vote(w3.cfx.epoch_number_by_tag("latest_state")) 48 | TypeValidator.validate_typed_dict(info, "DAOVoteInfo") 49 | 50 | def test_get_supply_info(self, w3: Web3): 51 | info = w3.cfx.get_supply_info() 52 | TypeValidator.validate_typed_dict(info, "SupplyInfo") 53 | 54 | def test_get_collateral_info(self, w3: Web3): 55 | info = w3.cfx.get_collateral_info() 56 | TypeValidator.validate_typed_dict(info, "CollateralInfo") 57 | info = w3.cfx.get_collateral_info("latest_confirmed") 58 | TypeValidator.validate_typed_dict(info, "CollateralInfo") 59 | # def test_get_account_pending_transactions(self, w3: Web3): 60 | # info = 61 | 62 | def test_get_fee_burnt(self, w3: Web3): 63 | fee_burnt = w3.cfx.get_fee_burnt() 64 | assert isinstance(fee_burnt, Drip) 65 | -------------------------------------------------------------------------------- /tests/rpcs/test_disabled_rpcs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from conflux_web3 import Web3 3 | from conflux_web3._utils.disabled_eth_apis import ( 4 | disabled_method_list, 5 | disabled_property_list 6 | ) 7 | from conflux_web3.exceptions import DisabledException 8 | 9 | def test_disabled_rpcs(w3: Web3): 10 | for method in disabled_method_list: 11 | getattr(w3.cfx, "generate_gas_price")() 12 | with pytest.raises(DisabledException): 13 | getattr(w3.cfx, method)() 14 | for property in disabled_property_list: 15 | with pytest.raises(DisabledException): 16 | getattr(w3.cfx, property) 17 | -------------------------------------------------------------------------------- /tests/rpcs/test_pending_rpcs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from typing import Any 3 | from conflux_web3 import Web3 4 | 5 | from conflux_web3.contract.metadata import get_contract_metadata 6 | from tests._test_helpers.type_check import TypeValidator 7 | 8 | class TestPending: 9 | @pytest.fixture(scope="class") 10 | def future_tx(self, moduled_w3: Web3): 11 | nonce = moduled_w3.cfx.get_next_nonce(moduled_w3.cfx.default_account) 12 | hash = moduled_w3.cfx.send_transaction({ 13 | "to": moduled_w3.account.create().address, 14 | "value": 100, 15 | "nonce": nonce + 1 16 | }) 17 | yield hash 18 | moduled_w3.cfx.send_transaction({ 19 | "to": moduled_w3.account.create().address, 20 | "value": 100, 21 | "nonce": nonce 22 | }) 23 | hash.executed() 24 | 25 | 26 | def test_get_account_pending_info(self, w3: Web3, address: str, future_tx: Any): 27 | account_pending_info = w3.cfx.get_account_pending_info(address) 28 | TypeValidator.validate_typed_dict(account_pending_info, "PendingInfo") 29 | 30 | 31 | def test_get_account_pending_transactions(self, w3: Web3, address:str , future_tx: Any): 32 | nonce = w3.cfx.get_next_nonce(address) 33 | info = w3.cfx.get_account_pending_transactions(address, nonce, 1) 34 | assert info["firstTxStatus"] == {"pending": "futureNonce"} 35 | assert info["pendingCount"] == 1 36 | for tx in info["pendingTransactions"]: 37 | TypeValidator.validate_tx_data(tx) 38 | -------------------------------------------------------------------------------- /tests/rpcs/test_txpool_rpcs.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | 3 | def test_next_nonce(w3: Web3, address: str): 4 | nonce = w3.txpool.next_nonce(address) 5 | assert isinstance(nonce, int) 6 | -------------------------------------------------------------------------------- /tests/test_node.py: -------------------------------------------------------------------------------- 1 | from os import stat 2 | import pytest 3 | 4 | from conflux_web3 import Web3 5 | 6 | 7 | def test_local_node(node, use_testnet): 8 | secrets = node.secrets 9 | if not use_testnet: 10 | assert (len(secrets) > 0) 11 | -------------------------------------------------------------------------------- /tests/transaction/contract/test_embed.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | 3 | 4 | from conflux_web3 import Web3 5 | from conflux_web3.contract.metadata import get_contract_metadata 6 | 7 | if TYPE_CHECKING: 8 | from conflux_web3 import Web3 9 | 10 | 11 | class TestEmbeddedContractMetadata: 12 | def test_get_contract_metadata(self): 13 | admin_contract_metadata = get_contract_metadata("AdminControl") 14 | assert isinstance(admin_contract_metadata["abi"], list) 15 | assert isinstance(admin_contract_metadata, dict) 16 | 17 | def test_contract_from_metadata(self, w3: Web3, use_testnet: bool): 18 | admin_contract = w3.cfx.contract(**get_contract_metadata("AdminControl")) 19 | assert admin_contract.abi 20 | assert w3.cfx.address.is_valid_base32(admin_contract.address) 21 | 22 | if use_testnet: 23 | 24 | usdt_contract = w3.cfx.contract( 25 | **get_contract_metadata("cUSDT", w3.cfx.chain_id) 26 | ) 27 | assert usdt_contract.abi 28 | assert usdt_contract.bytecode 29 | assert w3.cfx.address.is_valid_base32(usdt_contract.address) 30 | assert usdt_contract.caller.symbol() == "cUSDT" 31 | 32 | def test_contract_from_name(self, w3: Web3, use_testnet: bool): 33 | admin_contract = w3.cfx.contract(name="AdminControl") 34 | assert admin_contract.abi 35 | assert w3.cfx.address.is_valid_base32(admin_contract.address) 36 | 37 | if use_testnet: 38 | usdt_contract = w3.cfx.contract(name="cUSDT") 39 | assert usdt_contract.abi 40 | assert usdt_contract.bytecode 41 | assert w3.cfx.address.is_valid_base32(usdt_contract.address) 42 | assert usdt_contract.caller.symbol() == "cUSDT" 43 | 44 | if w3.cfx.chain_id == 1: 45 | faucet = w3.cfx.contract(name="Faucet") 46 | assert faucet 47 | 48 | # TODO: test all embedded metadata functionalities 49 | def test_faucet_functions(self, w3: Web3): 50 | if w3.cfx.chain_id == 1: 51 | random_account = w3.account.create() 52 | w3.cfx.default_account = random_account 53 | faucet = w3.cfx.contract(name="Faucet") 54 | faucet.functions.claimCfx().transact().executed() 55 | assert w3.cfx.get_balance(w3.cfx.default_account) > 0 56 | -------------------------------------------------------------------------------- /tests/transaction/contract/test_encoding.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | 3 | 4 | def test_encode_function(w3: Web3): 5 | contract = w3.cfx.contract(name="ERC20", with_deployment_info=False)("0x8888888888888888888888888888888888888888") 6 | account = w3.cfx.account.create() 7 | 8 | assert contract.functions.transfer(account.base32_address, 1000000000000000000).encode_transaction_data() == contract.functions.transfer(account.hex_address, 1000000000000000000).encode_transaction_data() 9 | -------------------------------------------------------------------------------- /tests/transaction/contract/test_erc20.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | import pytest 3 | 4 | 5 | from conflux_web3 import Web3 6 | from conflux_web3.contract import ( 7 | ConfluxContract, 8 | ) 9 | from conflux_web3.contract.metadata import get_contract_metadata 10 | 11 | from cfx_account import LocalAccount 12 | 13 | from conflux_web3.middleware.wallet import Wallet 14 | from tests._test_helpers.type_check import TypeValidator 15 | 16 | from conflux_web3.utils.address import get_create_address 17 | 18 | if TYPE_CHECKING: 19 | from conflux_web3 import Web3 20 | 21 | 22 | class TestERC20Contract: 23 | contract: ConfluxContract 24 | 25 | @pytest.fixture 26 | def w3_(self, w3: Web3, account): 27 | """w3 with wallet""" 28 | w3.cfx.default_account = account 29 | w3.middleware_onion.add(Wallet(account)) 30 | return w3 31 | 32 | # warnings might be raised by web3.py, we just ignore these warnings 33 | 34 | def test_contract_deploy_and_transfer(self, w3_: Web3): 35 | # test deployment 36 | erc20_metadata = get_contract_metadata("ERC20") 37 | erc20 = w3_.cfx.contract( 38 | bytecode=erc20_metadata["bytecode"], abi=erc20_metadata["abi"] 39 | ) 40 | tx_hash = erc20.constructor( 41 | name="Coin", symbol="C", initialSupply=10**18 42 | ).transact() 43 | contract_address = tx_hash.executed()["contractCreated"] 44 | tx_data = w3_.cfx.get_transaction(tx_hash) 45 | computed_contract_address = get_create_address(w3_.cfx.default_account, tx_data["nonce"], w3_.keccak(tx_data["data"])) # type: ignore 46 | assert contract_address == computed_contract_address, ( 47 | contract_address, 48 | computed_contract_address, 49 | ) 50 | 51 | contract = w3_.cfx.contract(contract_address, abi=erc20_metadata["abi"]) 52 | 53 | # test transfer 54 | random_account = w3_.account.create() 55 | hash = contract.functions.transfer(random_account.address, 100).transact() 56 | transfer_receipt = w3_.cfx.wait_for_transaction_receipt(hash) 57 | balance = contract.functions.balanceOf(random_account.address).call() 58 | assert balance == 100 59 | 60 | # test contract caller 61 | balance1 = contract.caller().balanceOf(random_account.address) 62 | assert balance1 == 100 63 | 64 | # test getLogs 65 | from_epoch = transfer_receipt["epochNumber"] 66 | logs = w3_.cfx.get_logs(fromEpoch=from_epoch, address=contract_address) 67 | for log in logs: 68 | TypeValidator.validate_typed_dict(log, "LogReceipt") 69 | 70 | # test contract event 71 | processed_log = contract.events.Transfer.process_receipt(transfer_receipt)[0] 72 | assert processed_log["args"]["from"] == w3_.cfx.default_account, processed_log 73 | assert processed_log["args"]["to"] == random_account.address, processed_log 74 | assert processed_log["args"]["value"] == 100, processed_log 75 | assert processed_log["blockHash"] == logs[0]["blockHash"], processed_log 76 | assert processed_log["epochNumber"] == logs[0]["epochNumber"], processed_log 77 | assert ( 78 | processed_log["transactionHash"] == logs[0]["transactionHash"] 79 | ), processed_log 80 | assert ( 81 | processed_log["transactionLogIndex"] == logs[0]["transactionLogIndex"] 82 | ), processed_log 83 | assert ( 84 | processed_log["transactionIndex"] == logs[0]["transactionIndex"] 85 | ), processed_log 86 | 87 | # test event filters 88 | filter_topics = contract.events.Transfer.get_filter_topics( 89 | value=100, to=random_account.address 90 | ) 91 | assert filter_topics 92 | new_logs = w3_.cfx.get_logs(fromEpoch=from_epoch, topics=filter_topics) 93 | assert new_logs == logs 94 | 95 | # test event get_logs 96 | new_processed_logs = contract.events.Transfer.get_logs( 97 | argument_filters={"value": 100, "to": random_account.address}, 98 | from_epoch=from_epoch, 99 | ) 100 | assert new_processed_logs[0]["args"] == processed_log["args"] 101 | 102 | 103 | def test_contract_without_wallet(self, w3: Web3, account: LocalAccount): 104 | erc20_metadata = get_contract_metadata("ERC20") 105 | 106 | erc20 = w3.cfx.contract( 107 | bytecode=erc20_metadata["bytecode"], abi=erc20_metadata["abi"] 108 | ) 109 | # test raw 110 | prebuilt_tx_params = erc20.constructor( 111 | name="Coin", symbol="C", initialSupply=10**18 112 | ).build_transaction( 113 | { 114 | "from": account.address, 115 | # 'nonce': w3.cfx.get_next_nonce(account.address), 116 | # 'value': 0, 117 | # 'gas': 21000, 118 | # 'gasPrice': 10**9, 119 | # 'chainId': w3.cfx.chain_id, 120 | # 'epochHeight': w3.cfx.epoch_number 121 | } 122 | ) 123 | 124 | raw_constuct_tx = account.sign_transaction(prebuilt_tx_params).raw_transaction 125 | contract_address = w3.cfx.send_raw_transaction(raw_constuct_tx).executed()[ 126 | "contractCreated" 127 | ] 128 | assert contract_address 129 | 130 | contract_instance = w3.cfx.contract( 131 | address=contract_address, abi=erc20_metadata["abi"] 132 | ) 133 | prebuilt_transfer = contract_instance.functions.transfer( 134 | w3.account.create().address, 100 135 | ).build_transaction({"from": account.address}) 136 | raw_tx = account.sign_transaction(prebuilt_transfer).raw_transaction 137 | w3.cfx.send_raw_transaction(raw_tx).executed() 138 | -------------------------------------------------------------------------------- /tests/transaction/contract/test_others.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | import pytest 3 | import os 4 | import json 5 | 6 | from cfx_account import LocalAccount 7 | from conflux_web3 import Web3 8 | from conflux_web3.contract.metadata import get_contract_metadata 9 | from cfx_utils.exceptions import Base32AddressNotMatch 10 | 11 | 12 | if TYPE_CHECKING: 13 | from conflux_web3 import Web3 14 | 15 | 16 | def test_contract_initialization(w3: Web3): 17 | metadata = get_contract_metadata("AdminControl") 18 | chain_id = w3.cfx.chain_id 19 | metadata["address"] = w3.cfx.address(metadata["address"], chain_id + 1) 20 | 21 | with pytest.raises(Base32AddressNotMatch): 22 | w3.cfx.contract(**metadata) 23 | 24 | 25 | def test_contract_with_no_deployment_info(w3: Web3): 26 | c = w3.cfx.contract(name="AdminControl", with_deployment_info=False) 27 | assert not c.address 28 | 29 | 30 | # This is an error from upstream web3.py and will be fixed in https://github.com/ethereum/web3.py/issues/3482 31 | def test_get_function_by_signature(w3: Web3, account: LocalAccount): 32 | from tests._test_helpers.ENV_SETTING import HELPER_DIR 33 | with open(os.path.join(HELPER_DIR, "amb_metadata.json")) as f: 34 | metadata = json.load(f) 35 | contract = w3.cfx.contract(abi=metadata['abi'], bytecode=metadata["bin"]) 36 | # with pytest.raises(ValidationError): 37 | # contract.functions.identity(account.address, True) 38 | 39 | w3.cfx.default_account = account 40 | contract_addr = contract.constructor().transact().executed()["contractCreated"] 41 | assert contract_addr 42 | contract = contract(contract_addr) 43 | func = contract.get_function_by_signature('identity(address,bool)') 44 | assert func(account.address, True).call() == account.address # returned address should be in Base32 format 45 | assert contract.get_function_by_signature('identity(string,bool)')(account.address, True).call() == account.address 46 | assert contract.get_function_by_signature('identity(string,bool)')("conflux", True).call() == "conflux" 47 | -------------------------------------------------------------------------------- /tests/transaction/test_base_tx.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from typing import Sequence 3 | from cfx_account.account import LocalAccount 4 | from conflux_web3 import Web3 5 | from conflux_web3.types import Base32Address 6 | from tests._test_helpers.type_check import TypeValidator 7 | 8 | 9 | def test_send_raw_transaction(w3: Web3, account: LocalAccount): 10 | status = w3.cfx.get_status() 11 | 12 | addr = account.address 13 | 14 | tx = { 15 | 'from': addr, 16 | 'nonce': w3.cfx.get_next_nonce(addr), 17 | 'gas': 21000, 18 | 'to': "cfxtest:aamd4myx7f3en2yu95xye7zb78gws09gj2ykmv9p58", 19 | 'value': 100, 20 | 'gasPrice': 10*10**9, 21 | 'chainId': w3.cfx.chain_id, 22 | 'storageLimit': 0, 23 | 'epochHeight': status['epochNumber'] 24 | } 25 | signed = account.sign_transaction(tx) 26 | rawTx = signed.raw_transaction 27 | r = w3.cfx.send_raw_transaction(rawTx) 28 | assert isinstance(r, bytes) 29 | receipt = w3.cfx.wait_for_transaction_receipt(r) 30 | TypeValidator.validate_typed_dict(receipt, "TxReceipt") 31 | 32 | def test_basetx_estimate(w3: Web3, address: Base32Address): 33 | """ 34 | estimate RPC behaviour, see https://wiki.conflux123.xyz/books/conflux101/page/cfx-estimate 35 | """ 36 | # addr = w3.account.from_key(secret_key).address 37 | 38 | tx = { 39 | "from": address, 40 | "to": w3.account.create().address, 41 | "value": 100, 42 | "gasPrice": w3.cfx.gas_price 43 | } 44 | 45 | estimate = w3.cfx.estimate_gas_and_collateral(tx, w3.cfx.epoch_number-5) 46 | assert estimate['gasUsed'] == 21000 47 | assert estimate["storageCollateralized"] == 0 48 | TypeValidator.validate_typed_dict(estimate, "EstimateResult") 49 | 50 | 51 | def test_send_transaction(w3: Web3, embedded_accounts: Sequence[Base32Address]): 52 | """test "cfx_sendTransaction" 53 | only works for local node if no middleware is activated 54 | """ 55 | # check if is a local node 56 | if len(embedded_accounts) == 0: 57 | return 58 | tx = { 59 | 'from': embedded_accounts[0], 60 | # 'nonce': w3.cfx.get_next_nonce(addr), 61 | # 'gas': 21000, 62 | 'to': w3.cfx.account.create().address, 63 | 'value': 100, 64 | 'gasPrice': 10**9, 65 | # 'chainId': w3.cfx.chain_id, 66 | # 'storageLimit': 0, 67 | # 'epochHeight': status['epochNumber'] 68 | } 69 | 70 | hash = w3.cfx.send_transaction(tx) 71 | assert hash 72 | w3.cfx.wait_for_transaction_receipt(hash) 73 | -------------------------------------------------------------------------------- /tests/transaction/test_tx_mungers.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from conflux_web3 import Web3 3 | 4 | def test_send_transaction_munger_exception(w3: Web3, address): 5 | 6 | # no "from" field 7 | with pytest.raises(ValueError): 8 | tx = { 9 | "to": w3.account.create().address 10 | } 11 | w3.cfx.send_transaction_munger(tx) 12 | try: 13 | # no exception here 14 | tx = { 15 | "to": w3.account.create().address 16 | } 17 | w3.cfx.default_account = address 18 | w3.cfx.send_transaction_munger(tx) 19 | 20 | # no exception here 21 | tx = { 22 | "to": w3.account.create().address, 23 | "from": address 24 | } 25 | w3.cfx.remove_default_account() 26 | w3.cfx.send_transaction_munger(tx) 27 | 28 | # no exception here 29 | tx = tx = { 30 | "to": w3.account.create().address, 31 | "nonce": 0 32 | } 33 | w3.cfx.send_transaction_munger(tx) 34 | except: 35 | pytest.fail("unexpected error") 36 | 37 | 38 | 39 | # def test_send_transaction_munger_exception(w3: Web3, Web3, address): 40 | # tx = { 41 | # "to": w3.account.create().address 42 | # } 43 | # with pytest.raises(ValueError): 44 | # w3.cfx.send_transaction_munger(tx) 45 | 46 | 47 | 48 | def test_estimate_munger(w3: Web3): 49 | """if default_account is set, it will be set as tx's default from field 50 | """ 51 | tx = { 52 | # "from": addr, 53 | # "from": w3.account.create().address, 54 | "to": w3.account.create().address, 55 | "value": 10**9 56 | } 57 | random_account = w3.account.create() 58 | w3.cfx.default_account = random_account.address 59 | 60 | processed = w3.cfx.estimate_gas_and_collateral_munger(tx) 61 | assert processed[0]['from'] == random_account.address # type: ignore 62 | -------------------------------------------------------------------------------- /tests/type_hints/test_contract_type_hint.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from conflux_web3 import Web3 3 | 4 | def test_contract_type_hint(w3: Web3): 5 | # the following code will never be actually executed 6 | if TYPE_CHECKING: 7 | addr = w3.address.zero_address(w3.cfx.chain_id) 8 | # should be ConfluxContract 9 | erc20 = w3.cfx.contract(name="ERC20", with_deployment_info=True) 10 | erc20 = w3.cfx.contract(addr, name="ERC20", with_deployment_info=True) 11 | erc20 = w3.cfx.contract(addr, name="ERC20", with_deployment_info=False) 12 | erc20 = w3.cfx.contract(addr) 13 | 14 | # should be Type[ConfluxContract] 15 | erc20 = w3.cfx.contract(name="ERC20", with_deployment_info=False) 16 | erc20(addr) 17 | erc20 = w3.cfx.contract() 18 | erc20(addr) 19 | 20 | # should be ConfluxContract | Type[ConfluxContract] 21 | erc20 = w3.cfx.contract(name="ERC20") 22 | -------------------------------------------------------------------------------- /tests/utils/test_contract_utils.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | from conflux_web3.types import Base32Address 3 | from cfx_address.utils import normalize_to 4 | from conflux_web3.utils.address import get_create_address, get_create2_address 5 | 6 | bytecode = "0x608060405234801561001057600080fd5b506101a1806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80637cf5dab014610030575b600080fd5b61004a600480360381019061004591906100b1565b610060565b60405161005791906100ed565b60405180910390f35b600060018261006f9190610137565b9050919050565b600080fd5b6000819050919050565b61008e8161007b565b811461009957600080fd5b50565b6000813590506100ab81610085565b92915050565b6000602082840312156100c7576100c6610076565b5b60006100d58482850161009c565b91505092915050565b6100e78161007b565b82525050565b600060208201905061010260008301846100de565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101428261007b565b915061014d8361007b565b925082820190508082111561016557610164610108565b5b9291505056fea26469706673582212208eb410cb79fbf08652e19d496e31d076d04be7ed242c64a44aec4c5af0f2533b64736f6c63430008130033" 7 | 8 | def test_get_create_address(): 9 | address = "cfx:aamz08kfa8wsu69jhhcgrwkjkh69p85wj6222847yp" 10 | nonce = 1 11 | bytecode_hash = Web3.solidity_keccak(["bytes"], [bytecode]) 12 | assert get_create_address(address, nonce, bytecode_hash) == Base32Address("0x837f77a1e8da5b860905a07bc1921e43fbfb04ef", 1029) 13 | assert get_create_address(Base32Address(address).hex_address, nonce, bytecode_hash) == normalize_to("0x837f77a1e8da5b860905a07bc1921e43fbfb04ef", None) 14 | 15 | def test_get_create2_address(): 16 | salt = (1111).to_bytes(32, 'big') 17 | bytecode_hash = Web3.solidity_keccak(["bytes"], [bytecode]) 18 | create2_factory_address = "0x8A3A92281Df6497105513B18543fd3B60c778E40" 19 | assert get_create2_address(create2_factory_address, salt, bytecode_hash) == normalize_to("0x80ac53cc16c0b58dc5bde5af47f5ef9e84693fe4", None) 20 | assert get_create2_address(Base32Address(create2_factory_address, 1029), salt, bytecode_hash) == normalize_to("0x80ac53cc16c0b58dc5bde5af47f5ef9e84693fe4", 1029) 21 | -------------------------------------------------------------------------------- /tests/utils/test_gas_utils.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | 3 | def test_generate_gas_price(w3: Web3): 4 | assert w3.cfx.generate_gas_price() is None 5 | -------------------------------------------------------------------------------- /tests/utils/test_test_utils.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | List, 3 | Union, 4 | Sequence, 5 | ) 6 | from typing_extensions import ( 7 | TypedDict, 8 | ) 9 | from tests._test_helpers.type_check import ( 10 | TypeValidator 11 | ) 12 | 13 | def test_base_check(): 14 | val = 1 15 | typ = int 16 | assert TypeValidator.isinstance(val, typ) 17 | 18 | def test_union_check(): 19 | vs= [1, "2"] 20 | typ = Union[int, str] 21 | 22 | for v in vs: 23 | assert TypeValidator.isinstance(v, typ) 24 | 25 | def test_list_check(): 26 | vs = [ 27 | [1,2,3], 28 | ("2","3","4") 29 | ] 30 | wrong_vs = [ 31 | [1, "2", []] 32 | ] 33 | ts = [ 34 | List[Union[int, str]], 35 | Sequence[Union[int, str]] 36 | ] 37 | for v in vs: 38 | for t in ts: 39 | assert TypeValidator.isinstance(v, t) 40 | 41 | for v in wrong_vs: 42 | for t in ts: 43 | assert not TypeValidator.isinstance(v, t) 44 | 45 | class D(TypedDict): 46 | a: int 47 | b: str 48 | 49 | def test_typed_dict(): 50 | d1 = { 51 | "a": 1, 52 | "b": "2" 53 | } 54 | assert TypeValidator.isinstance(d1, D) 55 | d2 = { 56 | "a": 1, 57 | "b": 2 58 | } 59 | assert not TypeValidator.isinstance(d2, D) 60 | -------------------------------------------------------------------------------- /tests/utils/test_transaction_utils.py: -------------------------------------------------------------------------------- 1 | from conflux_web3 import Web3 2 | from conflux_web3._utils.transactions import fill_transaction_defaults 3 | from cfx_address import Base32Address 4 | from tests._test_helpers.type_check import TypeValidator 5 | from conflux_web3._utils.transactions import tell_transaction_type 6 | from cfx_address import Base32Address 7 | 8 | def test_fill_transaction_defaults(w3: Web3, address:str): 9 | """test inner util fill_transaction_defaults. 10 | """ 11 | w3.cfx.default_account = address 12 | # "from" field is required before using this util 13 | unfilled_tx = { 14 | "from": Base32Address(address), 15 | "to": w3.account.create().address 16 | } 17 | filled_tx = fill_transaction_defaults(w3, unfilled_tx) 18 | TypeValidator.validate_typed_dict(filled_tx, "CIP1559TxDict") 19 | 20 | unfilled_tx = { 21 | "from": Base32Address(address), 22 | "to": w3.account.create().address, 23 | "gasPrice": 10**9 24 | } 25 | filled_tx = fill_transaction_defaults(w3, unfilled_tx) 26 | TypeValidator.validate_typed_dict(filled_tx, "LegacyTxDict") 27 | 28 | def test_tell_transaction_type(address: str): 29 | 30 | assert tell_transaction_type({ 31 | "from": Base32Address(address), 32 | "to": Base32Address(address) 33 | }) == 2 34 | 35 | assert tell_transaction_type({ 36 | "from": Base32Address(address), 37 | "to": Base32Address(address), 38 | "gasPrice": 1 39 | }) == 0 40 | 41 | assert tell_transaction_type({ 42 | "from": Base32Address(address), 43 | "to": Base32Address(address), 44 | "maxFeePerGas": 1, 45 | }) == 2 46 | --------------------------------------------------------------------------------