text: from typing import (
      TYPE_CHECKING,
      Any,
      Callable,
  )
  
  from eth_utils.curried import (
      apply_formatter_at_index,
      apply_formatter_if,
      apply_formatters_to_dict,
      is_null,
      is_string,
  )
  from eth_utils.toolz import (
      complement,
      compose,
      curry,
      dissoc,
  )
  from hexbytes import (
      HexBytes,
  )
  
  from web3._utils.formatters import (
      hex_to_integer,
  )
  from web3._utils.rpc_abi import (
      RPC,
  )
  from web3.exceptions import (
      ExtraDataLengthError,
      ValidationError,
  )
  from web3.middleware.formatting import (
      construct_web3_formatting_middleware,
  )
  from web3.types import (
      FormattersDict,
      TxParams,
  )
  
  if TYPE_CHECKING:
      from web3 import Web3  # noqa: F401
  
  MAX_EXTRADATA_LENGTH = 32
  
  is_not_null = complement(is_null)
  
  to_integer_if_hex = apply_formatter_if(is_string, hex_to_integer)
  
  
  @curry
  def validate_chain_id(web3: "Web3", chain_id: int) -> int:
      if to_integer_if_hex(chain_id) == web3.eth.chain_id:
          return chain_id
      else:
          raise ValidationError(
              "The transaction declared chain ID %r, "
              "but the connected node is on %r" % (
                  chain_id,
                  web3.eth.chain_id,
              )
          )
  
  
  def check_extradata_length(val: Any) -> Any:
      if not isinstance(val, (str, int, bytes)):
          return val
      result = HexBytes(val)
      if len(result) > MAX_EXTRADATA_LENGTH:
          raise ExtraDataLengthError(
              "The field extraData is %d bytes, but should be %d. "
              "It is quite likely that you are connected to a POA chain. "
              "Refer to "
              "http://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority "
              "for more details. The full extraData is: %r" % (
                  len(result), MAX_EXTRADATA_LENGTH, result
              )
          )
      return val
  
  
  def transaction_normalizer(transaction: TxParams) -> TxParams:
      return dissoc(transaction, 'chainId')
  
  
  def transaction_param_validator(web3: "Web3") -> Callable[..., Any]:
      transactions_params_validators = {
          "chainId": apply_formatter_if(
              # Bypass `validate_chain_id` if chainId can't be determined
              lambda _: is_not_null(web3.eth.chain_id),
              validate_chain_id(web3),
          ),
      }
      return apply_formatter_at_index(
          apply_formatters_to_dict(transactions_params_validators),
          0
      )
  
  
  BLOCK_VALIDATORS = {
      'extraData': check_extradata_length,
  }
  
  
  block_validator = apply_formatter_if(
      is_not_null,
      apply_formatters_to_dict(BLOCK_VALIDATORS)
  )
  
  
  @curry
  def chain_id_validator(web3: "Web3") -> Callable[..., Any]:
      return compose(
          apply_formatter_at_index(transaction_normalizer, 0),
          transaction_param_validator(web3)
      )
  
  
  def build_validators_with_web3(w3: "Web3") -> FormattersDict:
      return dict(
          request_formatters={
              RPC.eth_sendTransaction: chain_id_validator(w3),
              RPC.eth_estimateGas: chain_id_validator(w3),
              RPC.eth_call: chain_id_validator(w3),
          },
          result_formatters={
              RPC.eth_getBlockByHash: block_validator,
              RPC.eth_getBlockByNumber: block_validator,
          },
      )
  
  
  validation_middleware = construct_web3_formatting_middleware(build_validators_with_web3)
  