Some of our services depend on a ethereum/polygon rpc provider, including wallet connect and catalyst, this dependency generates a single point of failure where any downtime of this provider is a downtime in our services. In order to make our system more resilient we need a way to distribute this charge over multiples provider and fallback any request those service that are down to other that is not
We proposed a Cloudflare worker that ack as a load balancer between any provider we chose
ProviderHandler400)503)4)4)
      500 go to step
        4)
      stateDiagram-v2
  [*] --> ProviderHandler: Ethereum RPC Request
  state check_provider <<choice>>
  state check_network <<choice>>
  state check_protocol <<choice>>
  state check_response <<choice>>
  ProviderList --> ProviderHandler
  ProviderHandler --> CheckRequest
  CheckRequest --> [*]: Invalid Request
  CheckRequest --> PopProviderFromList
  PopProviderFromList --> check_provider
  check_provider --> CheckNetwork: Provider Selected
  check_provider --> [*]: No more providers
  CheckNetwork --> check_network
  check_network --> PopProviderFromList: Network not supported
  check_network --> CheckProtocol: Network supported
  CheckProtocol --> check_protocol
  check_protocol --> PopProviderFromList: Protocol not supported
  check_protocol --> fetch: Protocol supported
  fetch --> check_response
  check_response --> PopProviderFromList: HTTP Code >= 500
  check_response --> [*]: HTTP Code < 500
    
      ProtocolUpgrade defines if a request should be done over https or should be
      upgraded to websocket
    
export enum ProtocolUpgrade {
  https = "https",
  websocket = "websocket"
}
    Network defines the target network
export enum Network {
  mainnet = "mainnet",
  ropsten = "ropsten",
  goerli = "goerli",
  rinkeby = "rinkeby",
  kovan = "kovan",
  polygon = "polygon",
  mumbai = "mumbai"
}
    
      Provider is the interface that needs to be implemented once per ethereum provider
    
export type ProviderURLOptions = {
  upgrade: ProtocolUpgrade
  network: Network
}
export interface Provider {
  /**
   * resolves the url to be fetched, if the network
   * or the protocol is not supported by the provider
   * should return `null`
   */
  getTargetUrl(options: ProviderURLOptions): string | null
}
    
      ProviderHandler handle a request and returns the response of the provider if the
      provider responds with a server error it's retry the request with a different provider if all
      providers fails responds with and error
    
export default class ProviderHandler {
  constructor(private providers: Provider[] = []) {}
  /**
   * Add a new provider
   */
  add(provider: Provider) {
    this.providers.push(provider)
    return this
  }
  /**
   * Check if the a request should be upgraded to websocket
   */
  getProtocol(request: Request): ProtocolUpgrade | null
  /**
   * Check the network at witch the current request is trying to reach
   */
  getNetwork(request: Request): Network | null
  /**
   * Retuers a response with custom CORS
   */
  cors = async (request: Request): Promise<Response> => {
    return new Response(null, {
      status: 204,
      headers: {
        // ADD CORS HEADERS
      }
    })
  }
  handle = async (request: Request, event: FetchEvent): Promise<Response> => {
    const method = request.method.toLowerCase()
    if (method === "options") {
      return this.cors(request)
    }
    const upgrade = this.getProtocol(request)
    if (method === "get" && upgrade !== ProtocolUpgrade.websocket) {
      return new Response(`Upgrade Required`, { status: 426 })
    }
    const network = this.getNetwork(request)
    if (!network) {
      return new Response("Unsupported network", { status: 404 })
    }
    const providers = shuffle(this.providers)
    for (const provider of providers) {
      const target = provider.getTargetUrl({ network, upgrade })
      if (target) {
        const response = await fetch(target, request)
        if (response.status >= 500) {
          console.error(
            `Error calling ${new URL(request.url).host}: ${response.status} ${response.statusText}`
          )
        } else {
          // do not modify request for websocket
          return response
        }
      }
    }
    return new Response("Providers unavailable", { status: 503 })
  }
}