PancakeSwap follow live trades programmatically

This is a minimal example code for showing live swaps happening on PancakeSwap and all other Uniswap v2 compatible DEXes on Binance Smart Chain.

  • The example scripts reads the swap stream of PancakeSwap pools

  • Displays human readable price of swaps

  • The example event reading method is not robust as it does not deal with minor blockchain reorganisations. See other examples in the Tutorials section how to handle this.

  • Uses public BNB Chain JSON-RPC endpoint by default

  • The public JSON-RPC endpoint is not very performant, so you should get your own JSON-RPC node for production usage

To run:

python scripts/pancakeswap-live-swaps-minimal.py

Example output:

Swap at block:30,239,875 buy price:2.64193807 LEE/USDT in tx 0x0b51a9f0a0f50c493111c29e670a419f400daba03e63b9360c52dcf6a3b16c20
Swap at block:30,239,875 sell price:236.64745674 WBNB/USDT in tx 0x13b65557c777ec26a52dd2e025ac22f8382c62262af8838dc7ebd13b2711765d
Swap at block:30,239,875 buy price:0.00000039 JGB/USDT in tx 0x13b65557c777ec26a52dd2e025ac22f8382c62262af8838dc7ebd13b2711765d
Swap at block:30,239,875 sell price:0.00000043 JGB/USDT in tx 0x8b54bc21666463b533a42264c5b5cf9090d91e9bbdbc9d24b1cc757a128cf67b
Swap at block:30,239,876 buy price:70.20751754 L3/USDT in tx 0xe9dc7b06729f563de7bc7c4412586c54a3e8f774aace636320bff06583f77b33
Swap at block:30,239,876 buy price:70.20771432 L3/USDT in tx 0xec507565a1d8e330b717f64b63a1d1f75ca5fed14aeca76cc27981da528d515e
"""A minimal example of reading PancakeSwap live swaps.

- Reads the swap stream of PancakeSwap pools

- Displays human readable price of swaps

- *This event reader is not robust* as it does not deal with minor blockchain reorganisations.
  See other examples in the Tutorials section how to handle this.

- Uses public BNB Chain JSON-RPC endpoint by default

To run:

.. code-block:: shell

    python scripts/pancakeswap-live-swaps-minimal.py

Example output:

.. code-block: text

    Swap block:30,238,558 tx:0x826a7b87f2155f90a2ce41a8c9eebc58532d36e2e707181e6471c0c29573a5ab sell price:0.00099721 Welle/USDT
    Swap block:30,238,558 tx:0x5cf346e19501bcf8aa428409d016390528e840c29a7758a4ba10f795c60bb18a buy price:12.20856495 RWT/USDT
    Swap block:30,238,558 tx:0x54e6edccaf39a753b732e2e9c09fa5220b373c1b5116016f0fb5b2796d1a3af5 sell price:240.90248007 WBNB/USDT
    Swap block:30,238,559 tx:0xc0bb97210da2fda1348f00e9daec9dbdd23c1dac50e6b44296c8fe4810a861fb buy price:0.06722527 TEA/USDT
    Swap block:30,238,559 tx:0x6df61a50ca1073a9698929873718b98cc2330c33b8225ddc5cd6cb66aac7fd63 buy price:0.07435398 TEA/USDT
    Swap block:30,238,559 tx:0x0b2f2bd819bcc19bbdabe7fb799e057c46ec6c50328dcbd7928a503046ef7da2 sell price:0.07993185 TEA/USDT
    Swap block:30,238,559 tx:0x0b2f2bd819bcc19bbdabe7fb799e057c46ec6c50328dcbd7928a503046ef7da2 sell price:0.07805091 TEA/USDT

"""

import os
import time
from functools import lru_cache

from web3 import HTTPProvider, Web3

from eth_defi.abi import get_contract
from eth_defi.chain import install_chain_middleware
from eth_defi.event_reader.filter import Filter
from eth_defi.event_reader.logresult import decode_log
from eth_defi.event_reader.reader import read_events, LogResult
from eth_defi.uniswap_v2.pair import fetch_pair_details, PairDetails


QUOTE_TOKENS = ["BUSD", "USDC", "USDT"]


@lru_cache(maxsize=100)
def fetch_pair_details_cached(web3: Web3, pair_address: str) -> PairDetails:
    return fetch_pair_details(web3, pair_address)


def main():
    json_rpc_url = os.environ.get("JSON_RPC_BINANCE", "https://bsc-dataseed.binance.org/")
    web3 = Web3(HTTPProvider(json_rpc_url))
    web3.middleware_onion.clear()
    install_chain_middleware(web3)

    # Read the prepackaged ABI files and set up event filter
    # for any Uniswap v2 like pool on BNB Smart Chain (not just PancakeSwap).
    #
    # We use ABI files distributed by SushiSwap project.
    #
    Pair = get_contract(web3, "sushi/UniswapV2Pair.json")

    filter = Filter.create_filter(address=None, event_types=[Pair.events.Swap])

    next_block = web3.eth.block_number

    print(f"Starting at block {next_block:,}")

    # Keep reading events as they land
    while True:
        start = next_block
        end = web3.eth.block_number

        evt: LogResult
        for evt in read_events(
            web3,
            start_block=start,
            end_block=end,
            filter=filter,
        ):
            decoded = decode_log(evt)

            # Swap() events are generated by UniswapV2Pool contracts
            pair = fetch_pair_details_cached(web3, decoded["address"])
            token0 = pair.token0
            token1 = pair.token1
            block_number = evt["blockNumber"]

            # Determine the human-readable order of token tickers
            if token0.symbol in QUOTE_TOKENS:
                base = token1  # token
                quote = token0  # stablecoin/BNB
                base_amount = decoded["args"]["amount1Out"] - decoded["args"]["amount1In"]
                quote_amount = decoded["args"]["amount0Out"] - decoded["args"]["amount0In"]
            else:
                base = token0  # stablecoin/BNB
                quote = token1  # token
                base_amount = decoded["args"]["amount0Out"] - decoded["args"]["amount0Out"]
                quote_amount = decoded["args"]["amount1Out"] - decoded["args"]["amount1Out"]

            # Calculate the price in Python Decimal class
            if base_amount and quote_amount:
                human_base_amount = base.convert_to_decimals(base_amount)
                human_quote_amount = quote.convert_to_decimals(quote_amount)
                price = human_quote_amount / human_base_amount

                if human_quote_amount > 0:
                    # We define selling when the stablecoin amount increases
                    # in the swap
                    direction = "sell"
                else:
                    direction = "buy"

                price = abs(price)

                print(f"Swap at block:{block_number:,} {direction} price:{price:,.8f} {base.symbol}/{quote.symbol} in tx {evt['transactionHash']}")
            else:
                # Swap() event from DEX that is not Uniswap v2 compatible
                # print(f"Swap block:{block_number:,} tx:{evt['transactionHash']} could not decode")
                pass

        else:
            # No event detected between these blocks
            print(".")

        next_block = end + 1

        # Sleep is not an ideal way to loop through blocks
        # and is here only for an example purpose
        time.sleep(5)


if __name__ == "__main__":
    main()