-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<ChainSpecData> | ||
|
||
/** | ||
* 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<BlockInfo> | ||
/** | ||
* @returns Latest known finalized block. | ||
*/ | ||
getFinalizedBlock: () => Promise<BlockInfo> | ||
|
||
/** | ||
* 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<BlockInfo[]> | ||
/** | ||
* @returns Array of `BlockInfo`, being the first element the latest | ||
* known best block, and the last element the latest known | ||
* finalized block. | ||
*/ | ||
getBestBlocks: () => Promise<BlockInfo[]> | ||
|
||
/** | ||
* 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<HexString[]> | ||
/** | ||
* Get Block Body (Promise-based) | ||
* | ||
* @param hash It can be a block hash, `"finalized"`, or `"best"` | ||
* @returns Block body. | ||
*/ | ||
getBlockBody: (hash: string) => Promise<HexString[]> | ||
|
||
/** | ||
* Get Block Header (Promise-based) | ||
* | ||
* @param hash It can be a block hash, `"finalized"` (default), or | ||
* `"best"` | ||
* @returns Block hash. | ||
*/ | ||
getBlockHeader: (hash?: string) => Promise<BlockHeader> | ||
|
||
/** | ||
* 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<TxFinalizedPayload> | ||
/** | ||
* 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<TxBroadcastEvent> | ||
|
||
/** | ||
* Returns an instance of a `TypedApi` | ||
* | ||
* @param descriptors Pass descriptors from `@polkadot-api/descriptors` | ||
* generated by `papi` CLI. | ||
*/ | ||
getTypedApi: <D extends Descriptors>(descriptors: D) => TypedApi<D> | ||
|
||
/** | ||
* 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<string>("system_version", []) | ||
* const myFancyThhing = await client._request< | ||
* { value: string }, | ||
* [id: number] | ||
* >("very_fancy", [1714]) | ||
* | ||
*/ | ||
_request: <Reply = any, Params extends Array<any> = any[]>( | ||
method: string, | ||
params: Params, | ||
) => Promise<Reply> | ||
} | ||
``` | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Runtime> & { | ||
latest: () => Promise<Runtime> | ||
} | ||
``` | ||
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<string, Record<string, ???>>`. 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<boolean> | ||
(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<boolean> | ||
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! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters