From 87ab55f31eb649f7f2155d07bd142226c2a19d7c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 20 Oct 2024 10:11:25 +0700 Subject: [PATCH 1/3] initialize observable client --- package.json | 1 + packages/app/package.json | 4 ++ packages/app/src/model/Api/index.ts | 38 ++++++++++--- yarn.lock | 85 ++++++++++++++++++++++++++++- 4 files changed, 120 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index e0723bc28..e7c1ccb34 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "mocha": "^10.4.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", + "rxjs": "^7.8.1", "sass": "^1.78.0", "tsup": "^8.0.2", "tsx": "^4.19.1", diff --git a/packages/app/package.json b/packages/app/package.json index 6dc05da35..72548554e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -19,6 +19,10 @@ "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.0", "@ledgerhq/hw-transport-webhid": "^6.29.2", + "@polkadot-api/metadata-builders": "^0.8.2", + "@polkadot-api/observable-client": "^0.5.11", + "@polkadot-api/substrate-client": "^0.2.2", + "@polkadot-api/ws-provider": "^0.3.2", "@polkadot-cloud/icons": "1.0.0", "@polkadot/api": "^12.0.2", "@polkadot/rpc-provider": "12.0.2", diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index dd2cece5e..0ece09360 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -18,6 +18,10 @@ import { SubscriptionsController } from 'controllers/Subscriptions'; import type { AnyJson } from '@w3ux/types'; import BigNumber from 'bignumber.js'; import type { ChainSpaceId, OwnerId } from 'types'; +import type { JsonRpcProvider } from '@polkadot-api/ws-provider/web'; +import { getWsProvider } from '@polkadot-api/ws-provider/web'; +import { createClient as createRawClient } from '@polkadot-api/substrate-client'; +import { getObservableClient } from '@polkadot-api/observable-client'; export class Api { // ------------------------------------------------------ @@ -36,12 +40,18 @@ export class Api { // The supplied chain id. #chainId: ChainId; - // API provider. + // Polkadot JS API provider. #provider: WsProvider; - // API instance. + // Polkadot JS API instance. #api: ApiPromise; + // PAPI Provider. + #papiProvider: JsonRpcProvider; + + // PAPI Instance. + #papi: AnyJson; + // The current RPC endpoint. #rpcEndpoint: string; @@ -85,6 +95,14 @@ export class Api { return this.#api; } + get papiProvider() { + return this.#papiProvider; + } + + get papi() { + return this.#papi; + } + get rpcEndpoint() { return this.#rpcEndpoint; } @@ -114,17 +132,23 @@ export class Api { // Initialize the API. async initialize() { try { - // Initialize provider. + // Initialize Polkadot JS API provider. this.#provider = new WsProvider(this.#rpcEndpoint); + // Initialize PAPI provider. + this.#papiProvider = getWsProvider(this.#rpcEndpoint); + // Tell UI api is connecting. this.dispatchEvent(this.ensureEventStatus('connecting')); - // Initialise api. + // Initialise Polkadot JS API. this.#api = new ApiPromise({ provider: this.provider }); + // Initialize PAPI Client. + this.#papi = getObservableClient(createRawClient(this.#papiProvider)); + // Initialise api events. - this.initApiEvents(); + this.initPolkadotJsApiEvents(); await this.#api.isReady; @@ -227,8 +251,8 @@ export class Api { // Event handling. // ------------------------------------------------------ - // Set up API event listeners. Relays information to `document` for the UI to handle. - async initApiEvents() { + // Set up Polkadot JS API event listeners. Relays information to `document` for the UI to handle. + async initPolkadotJsApiEvents() { this.#api.on('ready', async () => { this.dispatchEvent(this.ensureEventStatus('ready')); this.handleFetchChainData(); diff --git a/yarn.lock b/yarn.lock index f04a6f8c0..af05ed690 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1796,6 +1796,13 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/json-rpc-provider-proxy@npm:0.2.3": + version: 0.2.3 + resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.2.3" + checksum: 10c0/eeaa46751fba879c03bde24a06a9aa4941bd901d166e1c49ae8d3596b7c6e8a868d4f25ba75c4a9761283953c2511057a51dd3ae27e4c0d7823d55f55af384b9 + languageName: node + linkType: hard + "@polkadot-api/json-rpc-provider-proxy@npm:^0.1.0": version: 0.1.0 resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.1.0" @@ -1817,6 +1824,13 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/json-rpc-provider@npm:0.0.4": + version: 0.0.4 + resolution: "@polkadot-api/json-rpc-provider@npm:0.0.4" + checksum: 10c0/5615394380bff2d8885592c8001055f1b006e3dd2f650b6a0e41ec836d61e903d78cc4ed06297827020523b0eecc9dff0af661fed7ae82523c97e294a2dc2e27 + languageName: node + linkType: hard + "@polkadot-api/metadata-builders@npm:0.0.1": version: 0.0.1 resolution: "@polkadot-api/metadata-builders@npm:0.0.1" @@ -1847,6 +1861,16 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/metadata-builders@npm:0.8.2, @polkadot-api/metadata-builders@npm:^0.8.2": + version: 0.8.2 + resolution: "@polkadot-api/metadata-builders@npm:0.8.2" + dependencies: + "@polkadot-api/substrate-bindings": "npm:0.9.2" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/038b7d5ffce244a806dc85f28ac53bc05721bb4e2c67fce4d8268de816694895320831483460341effc945254d5faf2483e3277c408f7e9c3353f1318c7c65ca + languageName: node + linkType: hard + "@polkadot-api/observable-client@npm:0.1.0": version: 0.1.0 resolution: "@polkadot-api/observable-client@npm:0.1.0" @@ -1875,6 +1899,20 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/observable-client@npm:^0.5.11": + version: 0.5.11 + resolution: "@polkadot-api/observable-client@npm:0.5.11" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.8.2" + "@polkadot-api/substrate-bindings": "npm:0.9.2" + "@polkadot-api/utils": "npm:0.1.2" + peerDependencies: + "@polkadot-api/substrate-client": 0.2.2 + rxjs: ">=7.8.0" + checksum: 10c0/a1d7374ab73b801de4ea81f17eac716078c23921e481ca78708747a27fd057568ac36b3d5bc50e0f4b12831779c59e8527fa9509fd199ade37c2b961cdf1370d + languageName: node + linkType: hard + "@polkadot-api/substrate-bindings@npm:0.0.1": version: 0.0.1 resolution: "@polkadot-api/substrate-bindings@npm:0.0.1" @@ -1911,6 +1949,18 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/substrate-bindings@npm:0.9.2": + version: 0.9.2 + resolution: "@polkadot-api/substrate-bindings@npm:0.9.2" + dependencies: + "@noble/hashes": "npm:^1.4.0" + "@polkadot-api/utils": "npm:0.1.2" + "@scure/base": "npm:^1.1.7" + scale-ts: "npm:^1.6.0" + checksum: 10c0/90cd0176413881ad75e8387d492b4f5137a650b0ae72c78e74e0dfb90ace4105507bb917ef682e695baeb28956ecbfd2202ef1300e1b615785655ad5a8771162 + languageName: node + linkType: hard + "@polkadot-api/substrate-client@npm:0.0.1": version: 0.0.1 resolution: "@polkadot-api/substrate-client@npm:0.0.1" @@ -1935,6 +1985,16 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/substrate-client@npm:^0.2.2": + version: 0.2.2 + resolution: "@polkadot-api/substrate-client@npm:0.2.2" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/c41322f554a77a5983573007db933907c73728e9f32788ff49f0b00f9b57ee575571b4fd94f9ed3c9e121274445d3a3019fe6b3b4d127b39e377f950d4cbc61c + languageName: node + linkType: hard + "@polkadot-api/utils@npm:0.0.1": version: 0.0.1 resolution: "@polkadot-api/utils@npm:0.0.1" @@ -1956,6 +2016,24 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/utils@npm:0.1.2": + version: 0.1.2 + resolution: "@polkadot-api/utils@npm:0.1.2" + checksum: 10c0/530270141ab7a8d114aff68adabbc643a7b7f5abcfb974a5dac5044e1f5a459881f427e357a7eadfecf55847da5e48828be6dbcf502dd22e097c87546762a036 + languageName: node + linkType: hard + +"@polkadot-api/ws-provider@npm:^0.3.2": + version: 0.3.2 + resolution: "@polkadot-api/ws-provider@npm:0.3.2" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.3" + ws: "npm:^8.18.0" + checksum: 10c0/60eda0d826afa2708b46bbe2869d82b08c0ab491a60bd2171ed3635ea61adb995cf17464fcd87e248247ef477257287ea561f4e0edefa84b6256b1692e95cfc3 + languageName: node + linkType: hard + "@polkadot-cloud/icons@npm:1.0.0": version: 1.0.0 resolution: "@polkadot-cloud/icons@npm:1.0.0" @@ -1977,6 +2055,10 @@ __metadata: "@fortawesome/free-solid-svg-icons": "npm:^6.5.2" "@fortawesome/react-fontawesome": "npm:^0.2.0" "@ledgerhq/hw-transport-webhid": "npm:^6.29.2" + "@polkadot-api/metadata-builders": "npm:^0.8.2" + "@polkadot-api/observable-client": "npm:^0.5.11" + "@polkadot-api/substrate-client": "npm:^0.2.2" + "@polkadot-api/ws-provider": "npm:^0.3.2" "@polkadot-cloud/icons": "npm:1.0.0" "@polkadot/api": "npm:^12.0.2" "@polkadot/rpc-provider": "npm:12.0.2" @@ -9394,6 +9476,7 @@ __metadata: mocha: "npm:^10.4.0" prettier: "npm:^3.2.5" prettier-plugin-organize-imports: "npm:^3.2.4" + rxjs: "npm:^7.8.1" sass: "npm:^1.78.0" tsup: "npm:^8.0.2" tsx: "npm:^4.19.1" @@ -11813,7 +11896,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.18.0, ws@npm:^8.15.1, ws@npm:^8.16.0, ws@npm:^8.8.1": +"ws@npm:8.18.0, ws@npm:^8.15.1, ws@npm:^8.16.0, ws@npm:^8.18.0, ws@npm:^8.8.1": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: From bd16e1c3f1c48894c8adf998d00c7d9309a1dae8 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 20 Oct 2024 10:24:21 +0700 Subject: [PATCH 2/3] `papiClient`, comments, type alias --- packages/app/src/model/Api/index.ts | 25 +++++++++++++++++-------- packages/app/src/model/Api/types.ts | 4 ++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index 0ece09360..4ab4bb1c9 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -12,6 +12,7 @@ import type { ErrDetail, ApiInstanceId, APIChainSpecEventDetail, + PapiObservableClient, } from './types'; import { MetadataController } from 'controllers/Metadata'; import { SubscriptionsController } from 'controllers/Subscriptions'; @@ -50,7 +51,7 @@ export class Api { #papiProvider: JsonRpcProvider; // PAPI Instance. - #papi: AnyJson; + #papiClient: PapiObservableClient; // The current RPC endpoint. #rpcEndpoint: string; @@ -99,8 +100,8 @@ export class Api { return this.#papiProvider; } - get papi() { - return this.#papi; + get papiClient() { + return this.#papiClient; } get rpcEndpoint() { @@ -145,11 +146,16 @@ export class Api { this.#api = new ApiPromise({ provider: this.provider }); // Initialize PAPI Client. - this.#papi = getObservableClient(createRawClient(this.#papiProvider)); + this.#papiClient = getObservableClient( + createRawClient(this.#papiProvider) + ); - // Initialise api events. - this.initPolkadotJsApiEvents(); + // NOTE: Unlike Polkadot JS API, observable client does not have an asynchronous + // initialization stage that leads to `isReady`. If using observable client, we can + // immediately attempt to fetch the chainSpec via the client. + // Initialise Polkadot JS API events and wait until ready. + this.initPolkadotJsApiEvents(); await this.#api.isReady; // Set initialized flag. @@ -202,14 +208,17 @@ export class Api { } async handleFetchChainData() { - // Fetch chain spec. NOTE: This is a one-time fetch. It's currently not possible to update the - // chain spec without a refresh. + // Fetch chain spec from Polkadot JS API. + // + // NOTE: This is a one-time fetch. It's currently not possible to update the chain spec without + // a refresh. if (!this.chainSpec) { // Fetch chain spec. await this.fetchChainSpec(); // Fetch chain constants. this.fetchConsts(); } + const detail: APIChainSpecEventDetail = { chainSpaceId: this.chainSpaceId, ownerId: this.ownerId, diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index 3316dde01..edc87763d 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -6,6 +6,10 @@ import type { ChainId } from 'config/networks/types'; import type { MetadataVersion } from 'controllers/Metadata/types'; import type { ChainSpaceId, OwnerId } from 'types'; +// Alias PAPI client +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type PapiObservableClient = any; + // An id associated with an api instance. ChainState, ChainSpec, subscriptions, etc. all use this id // to associate with an api instance. export type ApiInstanceId = string; From a9e0036427d60d2fb060f5da96a244fcb7ebc433 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 20 Oct 2024 10:30:08 +0700 Subject: [PATCH 3/3] address ai feedback --- packages/app/src/model/Api/types.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index edc87763d..474f1564b 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -6,9 +6,8 @@ import type { ChainId } from 'config/networks/types'; import type { MetadataVersion } from 'controllers/Metadata/types'; import type { ChainSpaceId, OwnerId } from 'types'; -// Alias PAPI client -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type PapiObservableClient = any; +// TODO: Replace with actual PAPI client interface when available +export type PapiObservableClient = unknown; // An id associated with an api instance. ChainState, ChainSpec, subscriptions, etc. all use this id // to associate with an api instance.