diff --git a/docs/source/tutorials/multi-rpc-configuration.rst b/docs/source/tutorials/multi-rpc-configuration.rst index ddfec319..1a292b39 100644 --- a/docs/source/tutorials/multi-rpc-configuration.rst +++ b/docs/source/tutorials/multi-rpc-configuration.rst @@ -13,6 +13,8 @@ These instances support multiple JSON-RPC providers, mainly - Gracefully deal and retry JSON-RPC errors, even on a single RPC provider. If multiple providers are provided, then recover if the provider goes down for a longer period of time. + This is especially important if you use an RPC multiplexing service like (drpc)[https://drpc.org] + where you get a high failure rate of random requests. - Automatic fallback to another JSON-RPC provider when one fails, also known as "hot spare" or "hot switch" strategy in DevOps @@ -101,3 +103,21 @@ And then: from eth_defi.provider.multi_provider import create_multi_provider_web3 web3 = create_multi_provider_web3(os.environ["JSON_RPC_POLYGON"]) + +Typical retryable errors +------------------------ + +A typical recoverable RPC error looks like: + +.. code-block:: text + + Encountered JSON-RPC retryable error HTTPSConnectionPool(host='lb.drpc.org', port=443): Read timed out. (read timeout=10) + When calling method: eth_getLogs({'topics': [['0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118', '0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67', '0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde', '0x0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c']], 'fromBlock': '0xbd5345', 'toBlock': '0xbd5b14'},) + Retrying in 5.000000 seconds, retry #1 / 6 + +Another example, no idea what this error is: + +.. code-block:: text + + Encountered JSON-RPC retryable error {'message': 'IllegalStateException: No executor delegate for eth_getBlockByHash', 'code': -32005} + When calling method: eth_getBlockByHash('0x4b16e6e01697e7917639a5216495db14160bf7d0ee75ccc5c8cbb623feace9cf', False) \ No newline at end of file diff --git a/docs/source/tutorials/uniswap-v3-liquidity-analysis.ipynb b/docs/source/tutorials/uniswap-v3-liquidity-analysis.ipynb index 020ea7a4..31463586 100644 --- a/docs/source/tutorials/uniswap-v3-liquidity-analysis.ipynb +++ b/docs/source/tutorials/uniswap-v3-liquidity-analysis.ipynb @@ -8,17 +8,15 @@ "\n", "In this notebook we will show how to download liquidity events from Uniswap V3 to your computer as CSV files and use them to construct and analyse liquidity in each pool.\n", "\n", + "**Note**: This example is old. There are now better, newer, APIs for writing the event reader loop for JSON-RPC source. Check tutorials section in the documentation for more examples. This example is still ok starting point if you are new into blockchains. \n", + "\n", "* For more background information, see [this blog post about the topic](https://tradingstrategy.ai/blog/uniswap-data-research-with-jupyter-and-python)\n", "\n", "* You need to understand [Jupyter Notebook and Pandas basics](https://jakevdp.github.io/PythonDataScienceHandbook/)\n", "\n", "* You need to understand [Ethereum](https://github.com/ethereumbook/ethereumbook) and [Web3.py basics](https://web3py.readthedocs.io/)\n", "\n", - "* You will need to have [Ethereum API node and its JSON-RPC URL](https://ethereumnodes.com/) in order to pull out the data from Ethereum blockchain. The notebook will interactively ask you for your API key.\n", - "\n", - "* You can click *Launch binder* button on this documentation page to automatically open this notebook to be run in\n", - " [Binder cloud notebook service](https://mybinder.org/). Note that we recommend to run the notebook\n", - " on your local computer, as generated CSV needs are large and take long time to generate." + "* You will need to have [Ethereum API node and its JSON-RPC URL](https://ethereumnodes.com/) in order to pull out the data from Ethereum blockchain. The notebook will interactively ask you for your API key.\n" ] }, { @@ -32,16 +30,41 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-06T17:09:53.607397Z", + "start_time": "2024-09-06T17:09:48.515512Z" + } + }, "source": [ + "import os\n", + "\n", "from web3 import Web3, HTTPProvider\n", "\n", + "from eth_defi.utils import get_url_domain\n", + "from eth_defi.provider.multi_provider import create_multi_provider_web3\n", + "\n", "# Get your node JSON-RPC URL\n", - "json_rpc_url = input(\"Please enter your Ethereum mainnet JSON-RPC URL here\")\n", - "web3 = Web3(HTTPProvider(json_rpc_url))" - ] + "json_rpc_url = os.environ.get(\"JSON_RPC_ETHEREUM\") # You can pass as env var if running from command line using ipython\n", + "if not json_rpc_url:\n", + " json_rpc_url = input(\"Enter your Ethereum mainnet JSON-RPC URL here: \")\n", + "\n", + "# Create a fault tolerant/auto recovery Web3 RPC backend.\n", + "# This is especially important if you are using RPC multiplexing service like dRPC or Pokt\n", + "# See \"MEV protection and multiple JSON-RPCs configuration\" tutorial for more information.\n", + "web3 = create_multi_provider_web3(json_rpc_url.strip())\n", + "print(\"Using RPC server\", get_url_domain(json_rpc_url)) # Don't display API key as the part of URL path or query string" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using RPC server lb.drpc.org\n" + ] + } + ], + "execution_count": 11 }, { "cell_type": "markdown", @@ -54,27 +77,47 @@ }, { "cell_type": "code", - "execution_count": 39, - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2024-09-06T17:09:56.039163Z", + "start_time": "2024-09-06T17:09:53.623549Z" + } + }, + "source": [ + "from eth_defi.uniswap_v3.constants import UNISWAP_V3_FACTORY_CREATED_AT_BLOCK\n", + "from eth_defi.uniswap_v3.events import fetch_events_to_csv\n", + "from eth_defi.event_reader.json_state import JSONFileScanState\n", + "\n", + "# Take 100,000 blocks snapshot from the time Uniswap v3 was deployed\n", + "start_block = UNISWAP_V3_FACTORY_CREATED_AT_BLOCK\n", + "end_block = UNISWAP_V3_FACTORY_CREATED_AT_BLOCK + 100_000\n", + "\n", + "# Stores the last block number of event data we store\n", + "state = JSONFileScanState(\"/tmp/uniswap-v3-liquidity-scan.json\")\n", + "\n", + "fetch_events_to_csv(json_rpc_url, state, start_block=start_block, end_block=end_block)\n", + "\n", + "print(\"CSV files generated\")" + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "No previous scan done, starting fresh from block 12,369,621, total 100,000 blocks\n", - "Scanning block range 12,369,621 - 12,469,621\n" + "Restored previous scan state, data until block 12,469,621, we are skipping 100,000 blocks out of 100,000 total\n", + "Saving Uniswap v3 data for block range 12,469,621 - 12,469,621\n" ] }, { "data": { + "text/plain": [ + "0it [00:00, ?it/s]" + ], "application/vnd.jupyter.widget-view+json": { - "model_id": "4704b8d5743145c4be01348d056a6ae6", "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/100000 [00:00\n", "