Skip to content
This repository has been archived by the owner on Sep 14, 2023. It is now read-only.

Approach to Narrow RPC Typings / Differentiation #108

Closed
harrysolovay opened this issue Jun 14, 2022 · 1 comment · Fixed by #126
Closed

Approach to Narrow RPC Typings / Differentiation #108

harrysolovay opened this issue Jun 14, 2022 · 1 comment · Fixed by #126

Comments

@harrysolovay
Copy link
Contributor

harrysolovay commented Jun 14, 2022

How do we want to approach narrowly-typing RPC methods (defined in rpc/messages.ts)?

  • There is no means of narrowing a client's type to reflect a subset of common capabilities.
  • There is no means of specifying custom RPC methods

The rpc/messages.ts file defines a central lookup of RPC methods of Substrate. This lookup is simply a record of functions. Paraphrasing:

type MethodLookup = {
  ...
  chain_getBlock(hash?: U.HashHexString): T.Block;
  chain_getBlockHash(height?: number): U.HashHexString;
  chain_getHead: MethodLookup["chain_getBlockHash"];
  chain_getFinalizedHead(): U.HashHexString;
  chain_getFinalisedHead: MethodLookup["chain_getFinalizedHead"];
  chain_getHeader(hash?: U.HashHexString): T.Header;
  chain_subscribeAllHeads(): Subscription<T.Header>;
  ...
};

A few things to note:

  1. We utilize branded types such as HashHexString instead of string. The core assumption here is that we may want to validate inputs. Related discussion in Approach to Branded Types & RPC-related Validation #96.
  2. Aliased methods reference the lookup directly (chain_getFinalisedHead, for example).
  3. Methods which produce a subscription return a Subscription<T>, where T is a union of all possible notifications. A good example of a type applied to T is the notification type of chainHead_unstable_follow.

Should we...

  • ... rename MethodLookup to SubstrateRpcMethods?
  • ... split out any unstable methods into an UnstableMethods lookup?
  • ... provide out-of-the box lookups such as PolkadotRpcMethods and KusamaRpcMethods?

From these lookups, users can narrow their clients.

import * as C from "capi";

// TODO: `Beacon` branded string type
const POLKADOT_BEACON = "wss://rpc.polkadot.io" as C.Beacon<C.PolkadotRpcMethods>;
// ... or ...
const polkadotBeacon = C.beacon<C.PolkadotRpcMethods>("wss://rpc.polkadot.io");

// Produce a narrowly-typed `WsRpcClient`
const client = C.wsRpcClient(POLKADOT_BEACON);

We could of course abstract over the creation of well-known clients.

import * as C from "capi";

const polkadotClient = C.polkadotRpcClient();

This approach enables users to define custom RPC clients with ease.

interface MyCustomRpcMethods extends C.SubstrateRpcMethods {
  some_otherMethod(...myArgs: MyArgs): Subscription<MyNotification>;
}

const beacon = C.beacon<MyCustomRpcMethods>(myChainWsProxyUrl);

const rawClient = C.wsRpcClient(beacon);

This is of course a very rough approximation of a solution. I'd imagine there will be a bit of complexity in the generic piping of the beacon's lookup into the methods of client instances.

A good starting place is likely to fine-tune and break apart the lookups so that they're easier to compose within the type system (and some tsdocs, so that we get that attractive guided DX!).

We should also investigate the possibility of generating RPC method lookups per-chain (#109).

@harrysolovay
Copy link
Contributor Author

Going to create a new issue regarding narrow RPC types in particular. Problem of differentiation is addressed in #126.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants