diff --git a/docs/pages/client.md b/docs/pages/client.md new file mode 100644 index 00000000..aff8b170 --- /dev/null +++ b/docs/pages/client.md @@ -0,0 +1,134 @@ +# PolkadotClient + +`PolkadotClient` interface shapes the top-level API for `polkadot-api`. Once we get a client using `createClient` function, we'll find the following: + +```ts +interface PolkadotClient { + /** + * Retrieve the ChainSpecData as it comes from the + * [JSON-RPC spec](https://paritytech.github.io/json-rpc-interface-spec/api/chainSpec.html) + */ + getChainSpecData: () => Promise + + /** + * Observable that emits `BlockInfo` from the latest known finalized block. + * It's a multicast and stateful observable, that will synchronously replay + * its latest known state. + */ + finalizedBlock$: Observable + /** + * @returns Latest known finalized block. + */ + getFinalizedBlock: () => Promise + + /** + * Observable that emits an Array of `BlockInfo`, being the first element the + * latest known best block, and the last element the latest known finalized + * block. It's a multicast and stateful observable, that will synchronously + * replay its latest known state. This array is an immutable data structure; + * i.e. a new array is emitted at every event but the reference to its + * children are stable if the children didn't change. + * + * Note that subscribing to this observable already supersedes the need of + * subscribing to `finalizedBlock$`, since the last element of the array will + * be the latest known finalized block. + */ + bestBlocks$: Observable + /** + * @returns Array of `BlockInfo`, being the first element the latest + * known best block, and the last element the latest known + * finalized block. + */ + getBestBlocks: () => Promise + + /** + * Observable to watch Block Body. + * + * @param hash It can be a block hash, `"finalized"`, or `"best"` + * @returns Observable to watch a block body. There'll be just one event + * with the payload and the observable will complete. + */ + watchBlockBody: (hash: string) => Observable + /** + * Get Block Body (Promise-based) + * + * @param hash It can be a block hash, `"finalized"`, or `"best"` + * @returns Block body. + */ + getBlockBody: (hash: string) => Promise + + /** + * Get Block Header (Promise-based) + * + * @param hash It can be a block hash, `"finalized"` (default), or + * `"best"` + * @returns Block hash. + */ + getBlockHeader: (hash?: string) => Promise + + /** + * Broadcast a transaction (Promise-based) + * + * @param transaction SCALE-encoded tx to broadcast. + * @param at It can be a block hash, `"finalized"`, or `"best"`. + * That block will be used to verify the validity of + * the tx, retrieve the next nonce, + * and create the mortality taking that block into + * account. + */ + submit: ( + transaction: HexString, + at?: HexString, + ) => Promise + /** + * Broadcast a transaction and returns an Observable. The observable will + * complete as soon as the transaction is in a finalized block. + * + * @param transaction SCALE-encoded tx to broadcast. + * @param at It can be a block hash, `"finalized"`, or `"best"`. + * That block will be used to verify the validity of + * the tx, retrieve the next nonce, + * and create the mortality taking that block into + * account. + */ + submitAndWatch: ( + transaction: HexString, + at?: HexString, + ) => Observable + + /** + * Returns an instance of a `TypedApi` + * + * @param descriptors Pass descriptors from `@polkadot-api/descriptors` + * generated by `papi` CLI. + */ + getTypedApi: (descriptors: D) => TypedApi + + /** + * This will `unfollow` the provider, disconnect and error every subscription. + * After calling it nothing can be done with the client. + */ + destroy: () => void + + /** + * This API is meant as an "escape hatch" to allow access to debug endpoints + * such as `system_version`, and other useful endpoints that are not spec + * compliant. + * + * @example + * + * const systemVersion = await client._request("system_version", []) + * const myFancyThhing = await client._request< + * { value: string }, + * [id: number] + * >("very_fancy", [1714]) + * + */ + _request: = any[]>( + method: string, + params: Params, + ) => Promise +} +``` + +As one can note, `PolkadotClient` heavily relies on rxjs' `Observable`, used as well under the hood of Promise-based methods. Every method is fairly straight-forward and already documented exhaustively, except for `getTypedApi`. Let's dive into it. diff --git a/docs/pages/typed.md b/docs/pages/typed.md new file mode 100644 index 00000000..4d94f6ce --- /dev/null +++ b/docs/pages/typed.md @@ -0,0 +1,67 @@ +# TypedApi + +The `TypedApi` allows to interact with the runtime metadata easily and with a great developer experience. It'll allow to make storage calls, create transactions, etc. It uses the descriptors generated by PAPI CLI (see [Codegen](/codegen) section for a deeper explanation) to generate the types used at devel time. `TypedApi` object looks like: + +```ts +type TypedApi = { + query: StorageApi + tx: TxApi + event: EvApi + apis: RuntimeCallsApi + constants: ConstApi + runtime: RuntimeApi +} +``` + +Let's start with the simplest one, `runtime` field. It's just: + +```ts +type RuntimeApi = Observable & { + latest: () => Promise +} +``` + +It's an observable that holds the current runtime information for that specific client, with a `latest` function to be able to wait for the runtime to load (it'll be helpful for some functions that need a `Runtime`, see [this recipe](/recipes/upgrade)). + +All the other fields are a `Record>`. The first index defines the pallet that we're looking for, and the second one defines which query/tx/event/api/constant are we looking for inside that pallet. Let's see, one by one, what do we find inside of it! + +## isCompatible + +First of all, let's understand `isCompatible` field. It's under each query/tx/event/api/constant in any runtime. After generating the descriptors (see [Codegen](/codegen) section), we have a typed interface to every interaction with the chain. Nevertheless, breaking runtime upgrades might hit the runtime between developing and the runtime execution of your app. `isCompatible` enables you to check on runtime if there was a breaking upgrade that hit your particular method. + +Let's see its interface, and an example. + +```ts +interface IsCompatible { + (): Promise + (runtime: Runtime): boolean +} +``` + +For example, let's use `typedApi.query.System.Number`. It's a simple query, we'll see in the next pages how to interact with it. We're only interested on `isCompatible`. + +```ts +const query = typedApi.query.System.Number +const runtime = await typedApi.runtime.latest() // we already learnt about it! + +// in this case `isCompatible` returns a Promise +if (await query.isCompatible()) { + // do your stuff, the query is compatible +} else { + // the call is not compatible! + // keep an eye on what you do +} + +// another option would be to use the already loaded runtime +// in this case, `isCompatible` is sync, and returns a boolean +if (query.isCompatible(runtime)) { + // do your stuff, the query is compatible +} else { + // the call is not compatible! + // keep an eye on what you do +} +``` + +As you can see, `isCompatible` is really powerful since we can prepare for runtime upgrades seamlessly using PAPI. See [this recipe](/recipes/upgrade) for an example! + +Let's continue with the rest of the fields! diff --git a/vocs.config.tsx b/vocs.config.tsx index 9fe78d1f..2b2e52b0 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -15,6 +15,25 @@ export default defineConfig({ text: "Getting Started", link: "/getting-started", }, + { + text: "Top-level client", + items: [ + { + text: "PolkadotClient", + link: "/client", + }, + { + text: "Typed API", + link: "/typed", + items: [ + { + text: "Storage queries", + link: "/typed/queries", + }, + ], + }, + ], + }, { text: "Providers", link: "/providers",