From eb72b16b9d2b9ed0a255254316275eacfb1f4424 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 14 Nov 2024 19:01:27 +0700 Subject: [PATCH 01/84] init papi ws / sm, get chainSpec --- packages/app/package.json | 1 + packages/app/src/config/networks.ts | 24 +- packages/app/src/config/util.ts | 45 + packages/app/src/contexts/Api/defaults.ts | 10 + packages/app/src/contexts/Api/index.tsx | 31 +- packages/app/src/contexts/Api/types.ts | 6 +- packages/app/src/model/Api/index.ts | 123 +- packages/app/src/model/Api/types.ts | 11 + packages/app/src/types.ts | 10 +- yarn.lock | 1281 ++++++++++++++++++++- 10 files changed, 1482 insertions(+), 60 deletions(-) create mode 100644 packages/app/src/config/util.ts diff --git a/packages/app/package.json b/packages/app/package.json index 996f9f99a..c4b9eae02 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -52,6 +52,7 @@ "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "plugin-staking-api": "workspace:*", + "polkadot-api": "^1.7.3", "qrcode-generator": "1.4.4", "rc-slider": "^11.1.6", "react": "^18.3.1", diff --git a/packages/app/src/config/networks.ts b/packages/app/src/config/networks.ts index 116bafd56..1a2b16a83 100644 --- a/packages/app/src/config/networks.ts +++ b/packages/app/src/config/networks.ts @@ -17,7 +17,8 @@ export const NetworkList: Networks = { polkadot: { name: 'polkadot', endpoints: { - lightClient: 'polkadot', + lightClientKey: 'polkadot', + lightClient: async () => await import('polkadot-api/chains/polkadot'), defaultRpcEndpoint: 'Automata 1RPC', rpcEndpoints: { 'Automata 1RPC': 'wss://1rpc.io/dot', @@ -72,7 +73,8 @@ export const NetworkList: Networks = { kusama: { name: 'kusama', endpoints: { - lightClient: 'ksmcc3', + lightClientKey: 'ksmcc3', + lightClient: async () => await import('polkadot-api/chains/ksmcc3'), defaultRpcEndpoint: 'Automata 1RPC', rpcEndpoints: { 'Automata 1RPC': 'wss://1rpc.io/ksm', @@ -127,7 +129,8 @@ export const NetworkList: Networks = { westend: { name: 'westend', endpoints: { - lightClient: 'westend2', + lightClientKey: 'westend2', + lightClient: async () => await import('polkadot-api/chains/westend2'), defaultRpcEndpoint: 'Automata 1RPC', rpcEndpoints: { Dwellir: 'wss://westend-rpc.dwellir.com', @@ -187,11 +190,14 @@ export const SystemChainList: Record = { units: 10, unit: 'DOT', endpoints: { - lightClient: 'polkadot_people', + lightClientKey: 'polkadot_people', + lightClient: async () => + await import('polkadot-api/chains/polkadot_people'), rpcEndpoints: { Parity: 'wss://polkadot-people-rpc.polkadot.io', }, }, + relayChain: 'polkadot', }, 'people-kusama': { name: 'people-kusama', @@ -199,11 +205,14 @@ export const SystemChainList: Record = { units: 12, unit: 'KSM', endpoints: { - lightClient: 'kusama_people', + lightClientKey: 'ksmcc3_people', + lightClient: async () => + await import('polkadot-api/chains/ksmcc3_people'), rpcEndpoints: { Parity: 'wss://kusama-people-rpc.polkadot.io', }, }, + relayChain: 'kusama', }, 'people-westend': { name: 'people-westend', @@ -211,10 +220,13 @@ export const SystemChainList: Record = { units: 12, unit: 'WND', endpoints: { - lightClient: 'westend_people', + lightClientKey: 'westend2_people', + lightClient: async () => + await import('polkadot-api/chains/westend2_people'), rpcEndpoints: { Parity: 'wss://westend-people-rpc.polkadot.io', }, }, + relayChain: 'westend', }, }; diff --git a/packages/app/src/config/util.ts b/packages/app/src/config/util.ts new file mode 100644 index 000000000..60d869153 --- /dev/null +++ b/packages/app/src/config/util.ts @@ -0,0 +1,45 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { ApiChainType } from 'model/Api/types'; +import type { AnyApi, NetworkName, SystemChainId } from 'types'; +import { NetworkList, SystemChainList } from './networks'; + +// Get the light client metadata for the given chain type and network. +export const getLightClientMetadata = ( + chainType: ApiChainType, + network: NetworkName | SystemChainId +): { + chain: { + key: string; + fn: () => Promise; + }; + relay?: { + key: string; + fn: () => Promise; + }; +} => { + if (chainType === 'relay') { + return { + chain: { + key: NetworkList[network].endpoints.lightClientKey, + fn: NetworkList[network].endpoints.lightClient, + }, + }; + } + + const { relayChain } = SystemChainList[network]; + const relay = NetworkList[relayChain]; + const system = SystemChainList[network]; + + return { + relay: { + key: relay.endpoints.lightClientKey, + fn: relay.endpoints.lightClient, + }, + chain: { + key: system.endpoints.lightClientKey, + fn: system.endpoints.lightClient, + }, + }; +}; diff --git a/packages/app/src/contexts/Api/defaults.ts b/packages/app/src/contexts/Api/defaults.ts index 09b5857f2..8d647590e 100644 --- a/packages/app/src/contexts/Api/defaults.ts +++ b/packages/app/src/contexts/Api/defaults.ts @@ -12,6 +12,7 @@ import type { APINetworkMetrics, APIPoolsConfig, APIStakingMetrics, + PapiChainSpecContext, } from 'contexts/Api/types'; export const defaultChainState: APIChainState = { @@ -43,6 +44,14 @@ export const defaultNetworkMetrics: APINetworkMetrics = { minimumActiveStake: new BigNumber(0), }; +export const defaultChainSpecs: PapiChainSpecContext = { + genesisHash: '', + ss58Format: 0, + tokenDecimals: 0, + tokenSymbol: '', + received: false, +}; + export const defaultActiveEra: APIActiveEra = { index: new BigNumber(0), start: new BigNumber(0), @@ -77,6 +86,7 @@ export const defaultApiContext: APIContextInterface = { api: null, peopleApi: null, chainState: defaultChainState, + chainSpecs: defaultChainSpecs, isReady: false, apiStatus: 'disconnected', peopleApiStatus: 'disconnected', diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 955615b7d..8ffa019c9 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -14,6 +14,7 @@ import type { APIPoolsConfig, APIProviderProps, APIStakingMetrics, + PapiChainSpecContext, } from './types'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { @@ -24,6 +25,7 @@ import { defaultPoolsConfig, defaultNetworkMetrics, defaultStakingMetrics, + defaultChainSpecs, } from './defaults'; import { isCustomEvent } from 'controllers/utils'; import { useEventListener } from 'usehooks-ts'; @@ -133,6 +135,10 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { ); const stakingMetricsRef = useRef(stakingMetrics); + // Store chain specs from PAPI. + const [chainSpecs, setChainSpecs] = + useState(defaultChainSpecs); + // Fetch chain state. Called once `provider` has been initialised. const onApiReady = async () => { const { api } = ApiController.get(network); @@ -220,6 +226,26 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { SubscriptionsController.set(network, 'activeEra', new ActiveEra(network)); }; + const handlePapiReady = (e: Event) => { + if (isCustomEvent(e)) { + const { chainType, genesisHash, ss58Format, tokenDecimals, tokenSymbol } = + e.detail; + + if (chainType === 'relay') { + const newChainSpecs: PapiChainSpecContext = { + genesisHash, + ss58Format, + tokenDecimals, + tokenSymbol, + received: true, + }; + setChainSpecs({ ...newChainSpecs, received: true }); + + // TODO: set consts from here. + } + } + }; + // Handle `polkadot-api` events. const handleNewApiStatus = (e: Event) => { if (isCustomEvent(e)) { @@ -434,6 +460,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { setRpcEndpoint(initialRpcEndpoint()); // Reset consts and chain state. + setChainSpecs(defaultChainSpecs); setConsts(defaultConsts); setChainState(defaultChainState); setStateWithRef( @@ -464,6 +491,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // Add event listener for api events and subscription updates. const documentRef = useRef(document); useEventListener('api-status', handleNewApiStatus, documentRef); + useEventListener('papi-ready', handlePapiReady, documentRef); useEventListener( 'new-network-metrics', handleNetworkMetricsUpdate, @@ -483,13 +511,14 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { api: ApiController.get(network)?.api || null, peopleApi: ApiController.get(`people-${network}`)?.api || null, chainState, + chainSpecs, apiStatus, peopleApiStatus, connectionType, setConnectionType, rpcEndpoint, setRpcEndpoint, - isReady: apiStatus === 'ready', + isReady: apiStatus === 'ready' && chainSpecs.received === true, consts, networkMetrics, activeEra, diff --git a/packages/app/src/contexts/Api/types.ts b/packages/app/src/contexts/Api/types.ts index ac7615cd8..0420ed7f8 100644 --- a/packages/app/src/contexts/Api/types.ts +++ b/packages/app/src/contexts/Api/types.ts @@ -5,7 +5,7 @@ import type { ApiPromise } from '@polkadot/api'; import type BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; import type { NetworkName } from '../../types'; -import type { ApiStatus, ConnectionType } from 'model/Api/types'; +import type { ApiStatus, ConnectionType, PapiChainSpec } from 'model/Api/types'; import type { AnyJson } from '@w3ux/types'; export interface APIProviderProps { @@ -40,6 +40,9 @@ export interface APINetworkMetrics { minimumActiveStake: BigNumber; } +export type PapiChainSpecContext = PapiChainSpec & { + received: boolean; +}; export interface APIActiveEra { index: BigNumber; start: BigNumber; @@ -74,6 +77,7 @@ export interface APIContextInterface { api: ApiPromise | null; peopleApi: ApiPromise | null; chainState: APIChainState; + chainSpecs: PapiChainSpecContext; isReady: boolean; apiStatus: ApiStatus; peopleApiStatus: ApiStatus; diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index f6c1374a1..e4dee3a01 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -9,17 +9,22 @@ import type { APIEventDetail, ConnectionType, EventApiStatus, + PapiChainSpec, + PapiReadyEvent, } from './types'; import { SubscriptionsController } from 'controllers/Subscriptions'; import { ScProvider } from '@polkadot/rpc-provider/substrate-connect'; import { WellKnownChain } from '@substrate/connect'; import * as Sc from '@substrate/connect'; +import type { PolkadotClient } from 'polkadot-api'; +import { createClient } from 'polkadot-api'; +import { getWsProvider } from 'polkadot-api/ws-provider/web'; +import { getSmProvider } from 'polkadot-api/sm-provider'; +import { startFromWorker } from 'polkadot-api/smoldot/from-worker'; +import SmWorker from 'polkadot-api/smoldot/worker?worker'; +import { getLightClientMetadata } from 'config/util'; export class Api { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The network name associated with this Api instance. network: NetworkName | SystemChainId; @@ -29,6 +34,12 @@ export class Api { // API provider. #provider: WsProvider | ScProvider; + // PAPI Instance. + #papiClient: PolkadotClient; + + // PAPI Chain Spec. + #papiChainSpec: PapiChainSpec; + // API instance. #api: ApiPromise; @@ -38,31 +49,27 @@ export class Api { // The current connection type. #connectionType: ConnectionType; - // ------------------------------------------------------ - // Getters. - // ------------------------------------------------------ - get api() { return this.#api; } + get papiClient() { + return this.#papiClient; + } + + get papiChainSpec() { + return this.#papiChainSpec; + } + get connectionType() { return this.#connectionType; } - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ - constructor(network: NetworkName | SystemChainId, chainType: ApiChainType) { this.network = network; this.#chainType = chainType; } - // ------------------------------------------------------ - // Initialization. - // ------------------------------------------------------ - // Class initialization. Sets the `provider` and `api` class members. async initialize(type: ConnectionType, rpcEndpoint: string) { // Set connection metadata. @@ -86,14 +93,19 @@ export class Api { // Tell UI api is connecting. this.dispatchEvent(this.ensureEventStatus('connecting')); - // Initialise api. + // Initialise Polkadot JS API. this.#api = new ApiPromise({ provider: this.#provider }); - // Initialise api events. + // NOTE: Unlike Polkadot JS API, Papi client does not have an asynchronous initialization + // stage that leads to `isReady`. If using papi client, we can immediately attempt to fetch + // the chainSpec via the client.∑ this.initApiEvents(); // Wait for api to be ready. await this.#api.isReady; + + // Fetch chain spec and metadata from PAPI client. + await this.fetchChainSpec(); } catch (e) { // TODO: report a custom api status error that can flag to the UI the rpcEndpoint failed - // retry or select another one. Useful for custom endpoint configs. @@ -101,10 +113,6 @@ export class Api { } } - // ------------------------------------------------------ - // Provider initialization. - // ------------------------------------------------------ - // Initiate Websocket Provider. initWsProvider() { const endpoint = @@ -114,7 +122,11 @@ export class Api { this.#rpcEndpoint ]; + // Initialize Polkadot JS Provider. this.#provider = new WsProvider(endpoint); + + // Initialize PAPI Client. + this.#papiClient = createClient(getWsProvider(endpoint)); } // Dynamically load and connect to Substrate Connect. @@ -122,20 +134,67 @@ export class Api { // Get light client key from network list. const lightClientKey = this.#chainType === 'relay' - ? NetworkList[this.network].endpoints.lightClient - : SystemChainList[this.network].endpoints.lightClient; + ? NetworkList[this.network].endpoints.lightClientKey + : SystemChainList[this.network].endpoints.lightClientKey; // Instantiate light client provider. this.#provider = new ScProvider( Sc as AnyApi, WellKnownChain[lightClientKey as keyof typeof WellKnownChain] ); + + // Initialise PAPI light client. + const smoldot = startFromWorker(new SmWorker()); + const smMetadata = getLightClientMetadata(this.#chainType, this.network); + + const chain = getSmProvider( + smoldot.addChain({ + chainSpec: await smMetadata.chain.fn(), + potentialRelayChains: smMetadata?.relay + ? [await smoldot.addChain({ chainSpec: await smMetadata.relay.fn() })] + : undefined, + }) + ); + this.#papiClient = createClient(chain); + + // Connect to Polkadot JS API provider. await this.#provider.connect(); } - // ------------------------------------------------------ - // Event handling. - // ------------------------------------------------------ + async fetchChainSpec() { + try { + const chainSpecData = await this.#papiClient.getChainSpecData(); + + const { genesisHash, properties } = chainSpecData; + const { ss58Format, tokenDecimals, tokenSymbol } = properties; + + this.#papiChainSpec = { + genesisHash, + ss58Format, + tokenDecimals, + tokenSymbol, + }; + + // Dispatch 'papi-ready' event to let contexts populate constants. + this.dispatchPapiReadyEvent(); + } catch (e) { + console.debug('PAPI chain spec failed'); + // TODO: Expand this when PJS API has been removed. Flag an error if there are any issues + // bootstrapping chain spec. NOTE: This can happen when PAPI is the standalone connection + // method. + //this.dispatchEvent(this.ensureEventStatus('error'), { err: 'ChainSpecError' }); + } + } + + // Handler for dispatching `papi-ready` events. + dispatchPapiReadyEvent() { + const detail: PapiReadyEvent = { + network: this.network, + chainType: this.#chainType, + ...this.#papiChainSpec, + }; + document.dispatchEvent(new CustomEvent('papi-ready', { detail })); + } // Set up API event listeners. Sends information to the UI. async initApiEvents() { @@ -156,7 +215,7 @@ export class Api { }); } - // Handler for dispatching events. + // Handler for dispatching `api-status` events. dispatchEvent( status: EventApiStatus, options?: { @@ -176,10 +235,6 @@ export class Api { document.dispatchEvent(new CustomEvent('api-status', { detail })); } - // ------------------------------------------------------ - // Class helpers. - // ------------------------------------------------------ - // Ensures the provided status is a valid `EventStatus` being passed, or falls back to `error`. ensureEventStatus = (status: string | EventApiStatus): EventApiStatus => { const eventStatus: string[] = [ @@ -207,10 +262,6 @@ export class Api { } }; - // ------------------------------------------------------ - // Disconnect. - // ------------------------------------------------------ - // Disconnect gracefully from API and provider. async disconnect(destroy = false) { this.unsubscribe(); diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index 5129cb463..eebef4ff4 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -17,6 +17,17 @@ export interface APIEventDetail { err?: string; } +export interface PapiChainSpec { + genesisHash: string; + ss58Format: number; + tokenDecimals: number; + tokenSymbol: string; +} +export type PapiReadyEvent = PapiChainSpec & { + network: NetworkName | SystemChainId; + chainType: string; +}; + export type ConnectionType = 'ws' | 'sc'; export type ApiStatus = 'connecting' | 'connected' | 'disconnected' | 'ready'; diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index 0a3b87cd4..e909ff810 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -16,7 +16,7 @@ import type { } from 'contexts/Api/types'; import type { SyncEvent } from 'controllers/Sync/types'; import type { DetailActivePool } from 'controllers/ActivePools/types'; -import type { APIEventDetail } from 'model/Api/types'; +import type { APIEventDetail, PapiReadyEvent } from 'model/Api/types'; import type { OnlineStatusEvent } from 'controllers/OnlineStatus/types'; import type { AnyJson } from '@w3ux/types'; import type { BlockNumberEventDetail } from 'model/Subscribe/BlockNumber/types'; @@ -30,6 +30,7 @@ declare global { interface DocumentEventMap { notification: CustomEvent; 'api-status': CustomEvent; + 'papi-ready': CustomEvent; 'online-status': CustomEvent; 'new-block-number': CustomEvent; 'new-network-metrics': CustomEvent<{ @@ -66,7 +67,8 @@ type NetworkColor = export interface Network { name: NetworkName; endpoints: { - lightClient: string; + lightClientKey: string; + lightClient: () => Promise; defaultRpcEndpoint: string; rpcEndpoints: Record; }; @@ -102,9 +104,11 @@ export interface SystemChain { units: number; unit: string; endpoints: { - lightClient: string; + lightClientKey: string; + lightClient: () => Promise; rpcEndpoints: Record; }; + relayChain: NetworkName; } export interface PageCategory { diff --git a/yarn.lock b/yarn.lock index 5401d1fb8..7eee8079c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,7 +59,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0": version: 7.26.2 resolution: "@babel/code-frame@npm:7.26.2" dependencies: @@ -259,6 +259,15 @@ __metadata: languageName: node linkType: hard +"@commander-js/extra-typings@npm:^12.1.0": + version: 12.1.0 + resolution: "@commander-js/extra-typings@npm:12.1.0" + peerDependencies: + commander: ~12.1.0 + checksum: 10c0/5d29eaa724b577e2a52a393ad54992924d2559931b8e493ab892477b7a4e878e475c6bf771260f8585d835f7d8e17ae4a2656c191e9595d210ae0b48291c0b3d + languageName: node + linkType: hard + "@dotlottie/common@npm:0.7.11": version: 0.7.11 resolution: "@dotlottie/common@npm:0.7.11" @@ -343,6 +352,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/aix-ppc64@npm:0.24.0" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm64@npm:0.21.5" @@ -350,6 +366,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/android-arm64@npm:0.24.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm@npm:0.21.5" @@ -357,6 +380,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/android-arm@npm:0.24.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-x64@npm:0.21.5" @@ -364,6 +394,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/android-x64@npm:0.24.0" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-arm64@npm:0.21.5" @@ -371,6 +408,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/darwin-arm64@npm:0.24.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-x64@npm:0.21.5" @@ -378,6 +422,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/darwin-x64@npm:0.24.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-arm64@npm:0.21.5" @@ -385,6 +436,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/freebsd-arm64@npm:0.24.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-x64@npm:0.21.5" @@ -392,6 +450,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/freebsd-x64@npm:0.24.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm64@npm:0.21.5" @@ -399,6 +464,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-arm64@npm:0.24.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm@npm:0.21.5" @@ -406,6 +478,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-arm@npm:0.24.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ia32@npm:0.21.5" @@ -413,6 +492,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-ia32@npm:0.24.0" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-loong64@npm:0.21.5" @@ -420,6 +506,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-loong64@npm:0.24.0" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-mips64el@npm:0.21.5" @@ -427,6 +520,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-mips64el@npm:0.24.0" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ppc64@npm:0.21.5" @@ -434,6 +534,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-ppc64@npm:0.24.0" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-riscv64@npm:0.21.5" @@ -441,6 +548,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-riscv64@npm:0.24.0" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-s390x@npm:0.21.5" @@ -448,6 +562,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-s390x@npm:0.24.0" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-x64@npm:0.21.5" @@ -455,6 +576,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/linux-x64@npm:0.24.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/netbsd-x64@npm:0.21.5" @@ -462,6 +590,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/netbsd-x64@npm:0.24.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/openbsd-arm64@npm:0.24.0" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/openbsd-x64@npm:0.21.5" @@ -469,6 +611,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/openbsd-x64@npm:0.24.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/sunos-x64@npm:0.21.5" @@ -476,6 +625,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/sunos-x64@npm:0.24.0" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-arm64@npm:0.21.5" @@ -483,6 +639,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/win32-arm64@npm:0.24.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-ia32@npm:0.21.5" @@ -490,6 +653,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/win32-ia32@npm:0.24.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-x64@npm:0.21.5" @@ -497,6 +667,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.24.0": + version: 0.24.0 + resolution: "@esbuild/win32-x64@npm:0.24.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.1 resolution: "@eslint-community/eslint-utils@npm:4.4.1" @@ -868,7 +1045,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.5": +"@jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.5 resolution: "@jridgewell/gen-mapping@npm:0.3.5" dependencies: @@ -1594,6 +1771,43 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/cli@npm:0.9.17": + version: 0.9.17 + resolution: "@polkadot-api/cli@npm:0.9.17" + dependencies: + "@commander-js/extra-typings": "npm:^12.1.0" + "@polkadot-api/codegen": "npm:0.12.7" + "@polkadot-api/ink-contracts": "npm:0.2.0" + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/known-chains": "npm:0.5.6" + "@polkadot-api/metadata-compatibility": "npm:0.1.11" + "@polkadot-api/observable-client": "npm:0.6.2" + "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" + "@polkadot-api/sm-provider": "npm:0.1.5" + "@polkadot-api/smoldot": "npm:0.3.5" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/substrate-client": "npm:0.3.0" + "@polkadot-api/utils": "npm:0.1.2" + "@polkadot-api/wasm-executor": "npm:^0.1.1" + "@polkadot-api/ws-provider": "npm:0.3.4" + "@types/node": "npm:^22.2.0" + commander: "npm:^12.1.0" + execa: "npm:^9.3.0" + fs.promises.exists: "npm:^1.1.4" + ora: "npm:^8.0.1" + read-pkg: "npm:^9.0.1" + rxjs: "npm:^7.8.1" + tsc-prog: "npm:^2.3.0" + tsup: "npm:^8.2.4" + typescript: "npm:^5.5.4" + write-package: "npm:^7.1.0" + bin: + papi: dist/main.js + polkadot-api: dist/main.js + checksum: 10c0/0bdbe91a11e07b0dcc730594c8adfe69fe7e522e7824f07feac51203dec1696e7ba050df209880567467b2813d20559ed85e23a62f71a179c348369f59574265 + languageName: node + linkType: hard + "@polkadot-api/client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 resolution: "@polkadot-api/client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" @@ -1608,6 +1822,43 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/codegen@npm:0.12.7": + version: 0.12.7 + resolution: "@polkadot-api/codegen@npm:0.12.7" + dependencies: + "@polkadot-api/ink-contracts": "npm:0.1.2" + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/metadata-compatibility": "npm:0.1.11" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/3330e74627123d24e0b9c572c172f832060d55b84e87986c8712ee17d9b84e299d90c3f06ff2b8aa1561a21219492bddce723e4c84e16c570f262028aea32333 + languageName: node + linkType: hard + +"@polkadot-api/ink-contracts@npm:0.1.2": + version: 0.1.2 + resolution: "@polkadot-api/ink-contracts@npm:0.1.2" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/utils": "npm:0.1.2" + scale-ts: "npm:^1.6.1" + checksum: 10c0/334203d4c981b691c8f0215746787a7664186c2947ba89818e58034643ab4f552ead612f8cf0b1f6cf29ad13696433717f01a75fde00fd6cd7a90cf0492e61f1 + languageName: node + linkType: hard + +"@polkadot-api/ink-contracts@npm:0.2.0": + version: 0.2.0 + resolution: "@polkadot-api/ink-contracts@npm:0.2.0" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/utils": "npm:0.1.2" + scale-ts: "npm:^1.6.1" + checksum: 10c0/c5f2457452047c0511bc3092cf61fbe1aafdc665fe5a8b60227fce57f587effea3e1f8d145ae3e6db1631a84286f40847cbf2c74f4f580e643151d2f09b92676 + languageName: node + linkType: hard + "@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" @@ -1615,6 +1866,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" @@ -1636,6 +1894,29 @@ __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/known-chains@npm:0.5.6": + version: 0.5.6 + resolution: "@polkadot-api/known-chains@npm:0.5.6" + checksum: 10c0/1767760ecdf834540290bdde8ab823907121b0ee3fee071bb393f00625b419299168c71cbcfaa6e6af67789cc5f4e4083a0020c955540b5f379b120895b44556 + languageName: node + linkType: hard + +"@polkadot-api/logs-provider@npm:0.0.6": + version: 0.0.6 + resolution: "@polkadot-api/logs-provider@npm:0.0.6" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + checksum: 10c0/0c7d22b7a9cc495539f3f4a61d0d77882139bcfbb8159d8ffadbc7f2b6a8dd093f217fd2266df60417bc7b23582d2c3659052e17bb9b1be664abf76edf7ce558 + languageName: node + linkType: hard + "@polkadot-api/merkleize-metadata@npm:^1.1.4": version: 1.1.9 resolution: "@polkadot-api/merkleize-metadata@npm:1.1.9" @@ -1677,6 +1958,30 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/metadata-compatibility@npm:0.1.11": + version: 0.1.11 + resolution: "@polkadot-api/metadata-compatibility@npm:0.1.11" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + checksum: 10c0/66397a323d272ea33fdf71fffd0ed4085d5a7d1333b07af21932cf19911cf8271905cc8489a7d40b67319ea83b5d5c2c056e692a2059c99ee22bec440167ae07 + languageName: node + linkType: hard + +"@polkadot-api/observable-client@npm:0.6.2": + version: 0.6.2 + resolution: "@polkadot-api/observable-client@npm:0.6.2" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/utils": "npm:0.1.2" + peerDependencies: + "@polkadot-api/substrate-client": 0.3.0 + rxjs: ">=7.8.0" + checksum: 10c0/dd5e767a16c70566776ee24aee62e1c4029b72bf1952d0b26e37da65b04c36c288a50ffdaf644b999d05fcb01da9e3aed4112b8ac0ef258d9d3135b4478d1ed0 + languageName: node + linkType: hard + "@polkadot-api/observable-client@npm:^0.3.0": version: 0.3.2 resolution: "@polkadot-api/observable-client@npm:0.3.2" @@ -1691,6 +1996,82 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/pjs-signer@npm:0.6.0": + version: 0.6.0 + resolution: "@polkadot-api/pjs-signer@npm:0.6.0" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/signers-common": "npm:0.1.1" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/efe86166d5f7f62cb9c94f555e56d56d2f73c2bef18cfb93a0e7004e0dc67d08abe0ec75712e03cb8506615718f17e06300dc7dfe50831fbbdd7286485223a24 + languageName: node + linkType: hard + +"@polkadot-api/polkadot-sdk-compat@npm:2.3.1": + version: 2.3.1 + resolution: "@polkadot-api/polkadot-sdk-compat@npm:2.3.1" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + checksum: 10c0/19f55588fbac5b72a8e0d54e7c5958b3de76907217bba94118aa409e86b1dc1a86f392da724a258a409678fd00ae21c07d0ade566a6238a84abd10cbed6952c9 + languageName: node + linkType: hard + +"@polkadot-api/polkadot-signer@npm:0.1.6": + version: 0.1.6 + resolution: "@polkadot-api/polkadot-signer@npm:0.1.6" + checksum: 10c0/26ab65ac02cfc9dcc8b9539f44f0e72f8faa8dbbb7cc68c5922e2ebdf71bcca0641e70f1fb0fec46de4f98f36448164c7025ef68bc21fa635b8bbb15f5731f10 + languageName: node + linkType: hard + +"@polkadot-api/signer@npm:0.1.10": + version: 0.1.10 + resolution: "@polkadot-api/signer@npm:0.1.10" + dependencies: + "@noble/hashes": "npm:^1.5.0" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/signers-common": "npm:0.1.1" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/25e2f5dcb02945aad2cb777e52f87a5dfcc0f0b46238717068419a9efa67f04f40de8ce05ec4ab4f046f028590f4729f2a0a019b44e8e3c8fa66a692713344de + languageName: node + linkType: hard + +"@polkadot-api/signers-common@npm:0.1.1": + version: 0.1.1 + resolution: "@polkadot-api/signers-common@npm:0.1.1" + dependencies: + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/17f1811e568ae3d655ae90dffdc3dd7327dc1d99673f6362f241c1e5595e188797755cb4b4bcbfae321385dadb38258b16725ea89e5de1fe8088a2dd227c9b34 + languageName: node + linkType: hard + +"@polkadot-api/sm-provider@npm:0.1.5": + version: 0.1.5 + resolution: "@polkadot-api/sm-provider@npm:0.1.5" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.3" + peerDependencies: + "@polkadot-api/smoldot": ">=0.3" + checksum: 10c0/d87dfc11d5d491c0bdb35e2a788641c8e1ec738d47885c1929c29d22ea03eaaf14a7ea355507b8817a42f9d37f0ae265ee138e6fd46618ca47cc6622046a302b + languageName: node + linkType: hard + +"@polkadot-api/smoldot@npm:0.3.5": + version: 0.3.5 + resolution: "@polkadot-api/smoldot@npm:0.3.5" + dependencies: + "@types/node": "npm:^22.2.0" + smoldot: "npm:2.0.31" + checksum: 10c0/a9658a06bf4a3bcb5127dcf3a58adf732c43be4c683cdd7054dadfb55122f32ce990aab866deb0a572340faadfdf9813ef2f938ae220be58628be67eeb14342d + languageName: node + linkType: hard + "@polkadot-api/substrate-bindings@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 resolution: "@polkadot-api/substrate-bindings@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" @@ -1734,6 +2115,16 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/substrate-client@npm:0.3.0": + version: 0.3.0 + resolution: "@polkadot-api/substrate-client@npm:0.3.0" + dependencies: + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/utils": "npm:0.1.2" + checksum: 10c0/a1eba558065f596f2fecf40d4bcef4e3ea50344b76bf5e890bbb17e4ff0b1fb251cf484bf2b8ef49ebcce5c2fc84ee1617e0769d2d6f740faf0caa623d8a6a12 + languageName: node + linkType: hard + "@polkadot-api/substrate-client@npm:^0.1.2": version: 0.1.4 resolution: "@polkadot-api/substrate-client@npm:0.1.4" @@ -1765,6 +2156,24 @@ __metadata: languageName: node linkType: hard +"@polkadot-api/wasm-executor@npm:^0.1.1": + version: 0.1.2 + resolution: "@polkadot-api/wasm-executor@npm:0.1.2" + checksum: 10c0/0577111cf7ee12e1606ba863385f50670f2d073e9032efd8365e578f03f4a8b01e699641e19b2aec15e36832aa3b6b0724951fa84d23e87492d0532f55e291bf + languageName: node + linkType: hard + +"@polkadot-api/ws-provider@npm:0.3.4": + version: 0.3.4 + resolution: "@polkadot-api/ws-provider@npm:0.3.4" + 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/39243a8f1545dd8191c62f43ee05e2450b5a7758adfe974f2d4a965546ea5001f19ff7ca14c87d1655dba1526ad08f8118a2cc6597f53d3a3172860b5da4d10d + languageName: node + linkType: hard + "@polkadot/api-augment@npm:10.13.1": version: 10.13.1 resolution: "@polkadot/api-augment@npm:10.13.1" @@ -2834,6 +3243,13 @@ __metadata: languageName: node linkType: hard +"@sec-ant/readable-stream@npm:^0.4.1": + version: 0.4.1 + resolution: "@sec-ant/readable-stream@npm:0.4.1" + checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -2841,6 +3257,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^4.0.0": + version: 4.0.0 + resolution: "@sindresorhus/merge-streams@npm:4.0.0" + checksum: 10c0/482ee543629aa1933b332f811a1ae805a213681ecdd98c042b1c1b89387df63e7812248bb4df3910b02b3cc5589d3d73e4393f30e197c9dde18046ccd471fc6b + languageName: node + linkType: hard + "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.2 resolution: "@socket.io/component-emitter@npm:3.1.2" @@ -3472,7 +3895,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*": +"@types/node@npm:*, @types/node@npm:^22.2.0": version: 22.9.0 resolution: "@types/node@npm:22.9.0" dependencies: @@ -3481,6 +3904,13 @@ __metadata: languageName: node linkType: hard +"@types/normalize-package-data@npm:^2.4.3": + version: 2.4.4 + resolution: "@types/normalize-package-data@npm:2.4.4" + checksum: 10c0/aef7bb9b015883d6f4119c423dd28c4bdc17b0e8a0ccf112c78b4fe0e91fbc4af7c6204b04bba0e199a57d2f3fbbd5b4a14bf8739bf9d2a39b2a0aad545e0f86 + languageName: node + linkType: hard + "@types/prop-types@npm:*": version: 15.7.13 resolution: "@types/prop-types@npm:15.7.13" @@ -4410,6 +4840,13 @@ __metadata: languageName: node linkType: hard +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + "anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -4460,6 +4897,7 @@ __metadata: lodash.debounce: "npm:^4.0.8" lodash.throttle: "npm:^4.1.1" plugin-staking-api: "workspace:*" + polkadot-api: "npm:^1.7.3" qrcode-generator: "npm:1.4.4" rc-slider: "npm:^11.1.6" react: "npm:^18.3.1" @@ -4802,6 +5240,17 @@ __metadata: languageName: node linkType: hard +"bundle-require@npm:^5.0.0": + version: 5.0.0 + resolution: "bundle-require@npm:5.0.0" + dependencies: + load-tsconfig: "npm:^0.2.3" + peerDependencies: + esbuild: ">=0.18" + checksum: 10c0/92c46df02586e0ebd66ee4831c9b5775adb3c32a43fe2b2aaf7bc675135c141f751de6a9a26b146d64c607c5b40f9eef5f10dce3c364f602d4bed268444c32c6 + languageName: node + linkType: hard + "cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" @@ -4919,6 +5368,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.3.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 10c0/8297d436b2c0f95801103ff2ef67268d362021b8210daf8ddbe349695333eb3610a71122172ff3b0272f1ef2cf7cc2c41fdaa4715f52e49ffe04c56340feed09 + languageName: node + linkType: hard + "chart.js@npm:^4.4.4": version: 4.4.6 resolution: "chart.js@npm:4.4.6" @@ -4956,6 +5412,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^4.0.1": + version: 4.0.1 + resolution: "chokidar@npm:4.0.1" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/4bb7a3adc304059810bb6c420c43261a15bb44f610d77c35547addc84faa0374265c3adc67f25d06f363d9a4571962b02679268c40de07676d260de1986efea9 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -4993,11 +5458,27 @@ __metadata: languageName: node linkType: hard -"clipboardy@npm:^4.0.0": - version: 4.0.0 - resolution: "clipboardy@npm:4.0.0" +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" dependencies: - execa: "npm:^8.0.1" + restore-cursor: "npm:^5.0.0" + checksum: 10c0/7ec62f69b79f6734ab209a3e4dbdc8af7422d44d360a7cb1efa8a0887bbe466a6e625650c466fe4359aee44dbe2dc0b6994b583d40a05d0808a5cb193641d220 + languageName: node + linkType: hard + +"cli-spinners@npm:^2.9.2": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 10c0/907a1c227ddf0d7a101e7ab8b300affc742ead4b4ebe920a5bf1bc6d45dce2958fcd195eb28fa25275062fe6fa9b109b93b63bc8033396ed3bcb50297008b3a3 + languageName: node + linkType: hard + +"clipboardy@npm:^4.0.0": + version: 4.0.0 + resolution: "clipboardy@npm:4.0.0" + dependencies: + execa: "npm:^8.0.1" is-wsl: "npm:^3.1.0" is64bit: "npm:^2.0.0" checksum: 10c0/02bb5f3d0a772bd84ec26a3566c72c2319a9f3b4cb8338370c3bffcf0073c80b834abe1a6945bea4f2cbea28e1627a975aaac577e3f61a868d924ce79138b041 @@ -5078,6 +5559,20 @@ __metadata: languageName: node linkType: hard +"commander@npm:^12.1.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 + languageName: node + linkType: hard + +"commander@npm:^4.0.0": + version: 4.1.1 + resolution: "commander@npm:4.1.1" + checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab + languageName: node + linkType: hard + "commander@npm:^8.0.0": version: 8.3.0 resolution: "commander@npm:8.3.0" @@ -5287,7 +5782,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.1, debug@npm:~4.3.2": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.7, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.7 resolution: "debug@npm:4.3.7" dependencies: @@ -5345,6 +5840,13 @@ __metadata: languageName: node linkType: hard +"deepmerge-ts@npm:^7.1.0": + version: 7.1.3 + resolution: "deepmerge-ts@npm:7.1.3" + checksum: 10c0/c9cfe7742a2c8f785302378b004381e1b831e3307ffe0c17be4b98fd87f347cb52a550aa9ff9ee0608b97f25400972ab79484f3836d77ec733828b10c8dcc522 + languageName: node + linkType: hard + "define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": version: 1.1.4 resolution: "define-data-property@npm:1.1.4" @@ -5402,6 +5904,13 @@ __metadata: languageName: node linkType: hard +"detect-indent@npm:^7.0.1": + version: 7.0.1 + resolution: "detect-indent@npm:7.0.1" + checksum: 10c0/47b6e3e3dda603c386e73b129f3e84844ae59bc2615f5072becf3cc02eab400bed5a4e6379c49d0b18cf630e80c2b07e87e0038b777addbc6ef793ad77dd05bc + languageName: node + linkType: hard + "detect-libc@npm:^1.0.3": version: 1.0.3 resolution: "detect-libc@npm:1.0.3" @@ -5522,6 +6031,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -5843,6 +6359,89 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.24.0": + version: 0.24.0 + resolution: "esbuild@npm:0.24.0" + dependencies: + "@esbuild/aix-ppc64": "npm:0.24.0" + "@esbuild/android-arm": "npm:0.24.0" + "@esbuild/android-arm64": "npm:0.24.0" + "@esbuild/android-x64": "npm:0.24.0" + "@esbuild/darwin-arm64": "npm:0.24.0" + "@esbuild/darwin-x64": "npm:0.24.0" + "@esbuild/freebsd-arm64": "npm:0.24.0" + "@esbuild/freebsd-x64": "npm:0.24.0" + "@esbuild/linux-arm": "npm:0.24.0" + "@esbuild/linux-arm64": "npm:0.24.0" + "@esbuild/linux-ia32": "npm:0.24.0" + "@esbuild/linux-loong64": "npm:0.24.0" + "@esbuild/linux-mips64el": "npm:0.24.0" + "@esbuild/linux-ppc64": "npm:0.24.0" + "@esbuild/linux-riscv64": "npm:0.24.0" + "@esbuild/linux-s390x": "npm:0.24.0" + "@esbuild/linux-x64": "npm:0.24.0" + "@esbuild/netbsd-x64": "npm:0.24.0" + "@esbuild/openbsd-arm64": "npm:0.24.0" + "@esbuild/openbsd-x64": "npm:0.24.0" + "@esbuild/sunos-x64": "npm:0.24.0" + "@esbuild/win32-arm64": "npm:0.24.0" + "@esbuild/win32-ia32": "npm:0.24.0" + "@esbuild/win32-x64": "npm:0.24.0" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/9f1aadd8d64f3bff422ae78387e66e51a5e09de6935a6f987b6e4e189ed00fdc2d1bc03d2e33633b094008529c8b6e06c7ad1a9782fb09fec223bf95998c0683 + languageName: node + linkType: hard + "escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -6275,6 +6874,26 @@ __metadata: languageName: node linkType: hard +"execa@npm:^9.3.0": + version: 9.5.1 + resolution: "execa@npm:9.5.1" + dependencies: + "@sindresorhus/merge-streams": "npm:^4.0.0" + cross-spawn: "npm:^7.0.3" + figures: "npm:^6.1.0" + get-stream: "npm:^9.0.0" + human-signals: "npm:^8.0.0" + is-plain-obj: "npm:^4.1.0" + is-stream: "npm:^4.0.1" + npm-run-path: "npm:^6.0.0" + pretty-ms: "npm:^9.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^4.0.0" + yoctocolors: "npm:^2.0.0" + checksum: 10c0/1a628d535c5a088f9e17a735bb3143efc4198095392b319ba877b2975d5c3c57724536dccb6f68f1cd9b3af331c5a9e8c1aeb338d52ab316b1e008ff453374a7 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -6356,6 +6975,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.4.2": + version: 6.4.2 + resolution: "fdir@npm:6.4.2" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/34829886f34a3ca4170eca7c7180ec4de51a3abb4d380344063c0ae2e289b11d2ba8b724afee974598c83027fea363ff598caf2b51bc4e6b1e0d8b80cc530573 + languageName: node + linkType: hard + "fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": version: 3.2.0 resolution: "fetch-blob@npm:3.2.0" @@ -6373,6 +7004,15 @@ __metadata: languageName: node linkType: hard +"figures@npm:^6.1.0": + version: 6.1.0 + resolution: "figures@npm:6.1.0" + dependencies: + is-unicode-supported: "npm:^2.0.0" + checksum: 10c0/9159df4264d62ef447a3931537de92f5012210cf5135c35c010df50a2169377581378149abfe1eb238bd6acbba1c0d547b1f18e0af6eee49e30363cedaffcfe4 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -6534,6 +7174,13 @@ __metadata: languageName: node linkType: hard +"fs.promises.exists@npm:^1.1.4": + version: 1.1.4 + resolution: "fs.promises.exists@npm:1.1.4" + checksum: 10c0/6e200a97e869c8b84b4dabc5c963d87d20db8704fa12098773b472a61651c0a822b651807ff883e09b8480c41f5184acb65abdd9965b950b3cb2b473b10c3c0f + languageName: node + linkType: hard + "fs.realpath@npm:^1.0.0": version: 1.0.0 resolution: "fs.realpath@npm:1.0.0" @@ -6600,6 +7247,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b + languageName: node + linkType: hard + "get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": version: 2.0.2 resolution: "get-func-name@npm:2.0.2" @@ -6634,6 +7288,16 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^9.0.0": + version: 9.0.1 + resolution: "get-stream@npm:9.0.1" + dependencies: + "@sec-ant/readable-stream": "npm:^0.4.1" + is-stream: "npm:^4.0.1" + checksum: 10c0/d70e73857f2eea1826ac570c3a912757dcfbe8a718a033fa0c23e12ac8e7d633195b01710e0559af574cbb5af101009b42df7b6f6b29ceec8dbdf7291931b948 + languageName: node + linkType: hard + "get-symbol-description@npm:^1.0.2": version: 1.0.2 resolution: "get-symbol-description@npm:1.0.2" @@ -6900,6 +7564,15 @@ __metadata: languageName: node linkType: hard +"hosted-git-info@npm:^7.0.0": + version: 7.0.2 + resolution: "hosted-git-info@npm:7.0.2" + dependencies: + lru-cache: "npm:^10.0.1" + checksum: 10c0/b19dbd92d3c0b4b0f1513cf79b0fc189f54d6af2129eeb201de2e9baaa711f1936929c848b866d9c8667a0f956f34bf4f07418c12be1ee9ca74fd9246335ca1f + languageName: node + linkType: hard + "howler@npm:^2.2.3": version: 2.2.4 resolution: "howler@npm:2.2.4" @@ -6964,6 +7637,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^8.0.0": + version: 8.0.0 + resolution: "human-signals@npm:8.0.0" + checksum: 10c0/e4dac4f7d3eb791ed04129fc6a85bd454a9102d3e3b76c911d0db7057ebd60b2956b435b5b5712aec18960488ede3c21ef7c56e42cdd70760c0d84d3c05cd92e + languageName: node + linkType: hard + "i18next-browser-languagedetector@npm:7.1.0": version: 7.1.0 resolution: "i18next-browser-languagedetector@npm:7.1.0" @@ -7078,6 +7758,13 @@ __metadata: languageName: node linkType: hard +"index-to-position@npm:^0.1.2": + version: 0.1.2 + resolution: "index-to-position@npm:0.1.2" + checksum: 10c0/7c91bde8bafc22684b74a7a24915bee4691cba48352ddb4ebe3b20a3a87bc0fa7a05f586137245ca8f92222a11f341f7631ff7f38cd78a523505d2d02dbfa257 + languageName: node + linkType: hard + "inflight@npm:^1.0.4": version: 1.0.6 resolution: "inflight@npm:1.0.6" @@ -7316,6 +8003,13 @@ __metadata: languageName: node linkType: hard +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: 10c0/801c8f6064f85199dc6bf99b5dd98db3282e930c3bc197b32f2c5b89313bb578a07d1b8a01365c4348c2927229234f3681eb861b9c2c92bee72ff397390fa600 + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -7360,6 +8054,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e + languageName: node + linkType: hard + "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -7400,6 +8101,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^4.0.1": + version: 4.0.1 + resolution: "is-stream@npm:4.0.1" + checksum: 10c0/2706c7f19b851327ba374687bc4a3940805e14ca496dc672b9629e744d143b1ad9c6f1b162dece81c7bfbc0f83b32b61ccc19ad2e05aad2dd7af347408f60c7f + languageName: node + linkType: hard + "is-string@npm:^1.0.5, is-string@npm:^1.0.7": version: 1.0.7 resolution: "is-string@npm:1.0.7" @@ -7427,6 +8135,20 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^1.3.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 10c0/b8674ea95d869f6faabddc6a484767207058b91aea0250803cbf1221345cb0c56f466d4ecea375dc77f6633d248d33c47bd296fb8f4cdba0b4edba8917e83d8a + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.2": version: 2.0.2 resolution: "is-weakmap@npm:2.0.2" @@ -7552,6 +8274,13 @@ __metadata: languageName: node linkType: hard +"joycon@npm:^3.1.1": + version: 3.1.1 + resolution: "joycon@npm:3.1.1" + checksum: 10c0/131fb1e98c9065d067fd49b6e685487ac4ad4d254191d7aa2c9e3b90f4e9ca70430c43cad001602bdbdabcf58717d3b5c5b7461c1bd8e39478c8de706b3fe6ae + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -7728,6 +8457,13 @@ __metadata: languageName: node linkType: hard +"lilconfig@npm:^3.1.1": + version: 3.1.2 + resolution: "lilconfig@npm:3.1.2" + checksum: 10c0/f059630b1a9bddaeba83059db00c672b64dc14074e9f232adce32b38ca1b5686ab737eb665c5ba3c32f147f0002b4bee7311ad0386a9b98547b5623e87071fbe + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -7795,6 +8531,13 @@ __metadata: languageName: node linkType: hard +"load-tsconfig@npm:^0.2.3": + version: 0.2.5 + resolution: "load-tsconfig@npm:0.2.5" + checksum: 10c0/bf2823dd26389d3497b6567f07435c5a7a58d9df82e879b0b3892f87d8db26900f84c85bc329ef41c0540c0d6a448d1c23ddc64a80f3ff6838b940f3915a3fcb + languageName: node + linkType: hard + "local-pkg@npm:^0.5.0": version: 0.5.0 resolution: "local-pkg@npm:0.5.0" @@ -7844,6 +8587,13 @@ __metadata: languageName: node linkType: hard +"lodash.sortby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.sortby@npm:4.7.0" + checksum: 10c0/fc48fb54ff7669f33bb32997cab9460757ee99fafaf72400b261c3e10fde21538e47d8cfcbe6a25a31bcb5b7b727c27d52626386fc2de24eb059a6d64a89cdf5 + languageName: node + linkType: hard + "lodash.throttle@npm:^4.1.1": version: 4.1.1 resolution: "lodash.throttle@npm:4.1.1" @@ -7851,6 +8601,16 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^6.0.0": + version: 6.0.0 + resolution: "log-symbols@npm:6.0.0" + dependencies: + chalk: "npm:^5.3.0" + is-unicode-supported: "npm:^1.3.0" + checksum: 10c0/36636cacedba8f067d2deb4aad44e91a89d9efb3ead27e1846e7b82c9a10ea2e3a7bd6ce28a7ca616bebc60954ff25c67b0f92d20a6a746bb3cc52c3701891f6 + languageName: node + linkType: hard + "loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -7995,6 +8755,13 @@ __metadata: languageName: node linkType: hard +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d + languageName: node + linkType: hard + "minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": version: 1.0.1 resolution: "minimalistic-assert@npm:1.0.1" @@ -8186,6 +8953,17 @@ __metadata: languageName: node linkType: hard +"mz@npm:^2.7.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: "npm:^1.0.0" + object-assign: "npm:^4.0.1" + thenify-all: "npm:^1.0.0" + checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" @@ -8350,6 +9128,17 @@ __metadata: languageName: node linkType: hard +"normalize-package-data@npm:^6.0.0": + version: 6.0.2 + resolution: "normalize-package-data@npm:6.0.2" + dependencies: + hosted-git-info: "npm:^7.0.0" + semver: "npm:^7.3.5" + validate-npm-package-license: "npm:^3.0.4" + checksum: 10c0/7e32174e7f5575ede6d3d449593247183880122b4967d4ae6edb28cea5769ca025defda54fc91ec0e3c972fdb5ab11f9284606ba278826171b264cb16a9311ef + languageName: node + linkType: hard + "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" @@ -8375,6 +9164,16 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^6.0.0": + version: 6.0.0 + resolution: "npm-run-path@npm:6.0.0" + dependencies: + path-key: "npm:^4.0.0" + unicorn-magic: "npm:^0.3.0" + checksum: 10c0/b223c8a0dcd608abf95363ea5c3c0ccc3cd877daf0102eaf1b0f2390d6858d8337fbb7c443af2403b067a7d2c116d10691ecd22ab3c5273c44da1ff8d07753bd + languageName: node + linkType: hard + "obj-multiplex@npm:^1.0.0": version: 1.0.0 resolution: "obj-multiplex@npm:1.0.0" @@ -8386,7 +9185,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.1": +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 @@ -8507,6 +9306,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10c0/5cb9179d74b63f52a196a2e7037ba2b9a893245a5532d3f44360012005c9cadb60851d56716ebff18a6f47129dab7168022445df47c2aff3b276d92585ed1221 + languageName: node + linkType: hard + "open@npm:^8.4.0": version: 8.4.2 resolution: "open@npm:8.4.2" @@ -8544,6 +9352,23 @@ __metadata: languageName: node linkType: hard +"ora@npm:^8.0.1": + version: 8.1.1 + resolution: "ora@npm:8.1.1" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^5.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.2" + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/996a81a9e997481339de3a7996c56131ea292c0a0e9e42d1cd454e2390f1ce7015ec925dcdd29e3d74dc5d037a4aa1877e575b491555507bcd9f219df760a63f + languageName: node + linkType: hard + "ox@npm:0.1.2": version: 0.1.2 resolution: "ox@npm:0.1.2" @@ -8660,6 +9485,24 @@ __metadata: languageName: node linkType: hard +"parse-json@npm:^8.0.0": + version: 8.1.0 + resolution: "parse-json@npm:8.1.0" + dependencies: + "@babel/code-frame": "npm:^7.22.13" + index-to-position: "npm:^0.1.2" + type-fest: "npm:^4.7.1" + checksum: 10c0/39a49acafc1c41a763df2599a826eb77873a44b098a5f2ba548843229b334a16ff9d613d0381328e58031b0afaabc18ed2a01337a6522911ac7a81828df58bcb + languageName: node + linkType: hard + +"parse-ms@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-ms@npm:4.0.0" + checksum: 10c0/a7900f4f1ebac24cbf5e9708c16fb2fd482517fad353aecd7aefb8c2ba2f85ce017913ccb8925d231770404780df46244ea6fec598b3bde6490882358b4d2d16 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -8799,6 +9642,13 @@ __metadata: languageName: node linkType: hard +"pirates@npm:^4.0.1": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 10c0/00d5fa51f8dded94d7429700fb91a0c1ead00ae2c7fd27089f0c5b63e6eca36197fe46384631872690a66f390c5e27198e99006ab77ae472692ab9c2ca903f36 + languageName: node + linkType: hard + "pkg-types@npm:^1.0.3, pkg-types@npm:^1.2.1": version: 1.2.1 resolution: "pkg-types@npm:1.2.1" @@ -8826,6 +9676,37 @@ __metadata: languageName: node linkType: hard +"polkadot-api@npm:^1.7.3": + version: 1.7.3 + resolution: "polkadot-api@npm:1.7.3" + dependencies: + "@polkadot-api/cli": "npm:0.9.17" + "@polkadot-api/ink-contracts": "npm:0.2.0" + "@polkadot-api/json-rpc-provider": "npm:0.0.4" + "@polkadot-api/known-chains": "npm:0.5.6" + "@polkadot-api/logs-provider": "npm:0.0.6" + "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/metadata-compatibility": "npm:0.1.11" + "@polkadot-api/observable-client": "npm:0.6.2" + "@polkadot-api/pjs-signer": "npm:0.6.0" + "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" + "@polkadot-api/polkadot-signer": "npm:0.1.6" + "@polkadot-api/signer": "npm:0.1.10" + "@polkadot-api/sm-provider": "npm:0.1.5" + "@polkadot-api/smoldot": "npm:0.3.5" + "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/substrate-client": "npm:0.3.0" + "@polkadot-api/utils": "npm:0.1.2" + "@polkadot-api/ws-provider": "npm:0.3.4" + peerDependencies: + rxjs: ">=7.8.0" + bin: + papi: bin/cli.mjs + polkadot-api: bin/cli.mjs + checksum: 10c0/ac430cd00d51308f65f3f8a1d7b405d3d1d86726c47481dfa2cfb2c08c36dda3cb51f0a880a00c7fa677e84bae3385de91e3da6627492fad89848b5a3e94b6fa + languageName: node + linkType: hard + "polkadot-staking-dashboard@workspace:.": version: 0.0.0-use.local resolution: "polkadot-staking-dashboard@workspace:." @@ -8874,6 +9755,29 @@ __metadata: languageName: node linkType: hard +"postcss-load-config@npm:^6.0.1": + version: 6.0.1 + resolution: "postcss-load-config@npm:6.0.1" + dependencies: + lilconfig: "npm:^3.1.1" + peerDependencies: + jiti: ">=1.21.0" + postcss: ">=8.0.9" + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + checksum: 10c0/74173a58816dac84e44853f7afbd283f4ef13ca0b6baeba27701214beec33f9e309b128f8102e2b173e8d45ecba45d279a9be94b46bf48d219626aa9b5730848 + languageName: node + linkType: hard + "postcss-value-parser@npm:^4.0.2": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" @@ -8960,6 +9864,15 @@ __metadata: languageName: node linkType: hard +"pretty-ms@npm:^9.0.0": + version: 9.1.0 + resolution: "pretty-ms@npm:9.1.0" + dependencies: + parse-ms: "npm:^4.0.0" + checksum: 10c0/fd111aad8800a04dfd654e6016da69bdaa6fc6a4c280f8e727cffd8b5960558e94942f1a94d4aa6e4d179561a0fbb0366a9ebe0ccefbbb0f8ff853b129cdefb9 + languageName: node + linkType: hard + "proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": version: 4.2.0 resolution: "proc-log@npm:4.2.0" @@ -9300,6 +10213,19 @@ __metadata: languageName: node linkType: hard +"read-pkg@npm:^9.0.1": + version: 9.0.1 + resolution: "read-pkg@npm:9.0.1" + dependencies: + "@types/normalize-package-data": "npm:^2.4.3" + normalize-package-data: "npm:^6.0.0" + parse-json: "npm:^8.0.0" + type-fest: "npm:^4.6.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/f3e27549dcdb18335597f4125a3d093a40ab0a18c16a6929a1575360ed5d8679b709b4a672730d9abf6aa8537a7f02bae0b4b38626f99409255acbd8f72f9964 + languageName: node + linkType: hard + "readable-stream@npm:^2.3.3": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" @@ -9339,6 +10265,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^4.0.1": + version: 4.0.2 + resolution: "readdirp@npm:4.0.2" + checksum: 10c0/a16ecd8ef3286dcd90648c3b103e3826db2b766cdb4a988752c43a83f683d01c7059158d623cbcd8bdfb39e65d302d285be2d208e7d9f34d022d912b929217dd + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -9425,6 +10358,13 @@ __metadata: languageName: node linkType: hard +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + "resolve-pkg-maps@npm:^1.0.0": version: 1.0.0 resolution: "resolve-pkg-maps@npm:1.0.0" @@ -9491,6 +10431,16 @@ __metadata: languageName: node linkType: hard +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" + dependencies: + onetime: "npm:^7.0.0" + signal-exit: "npm:^4.1.0" + checksum: 10c0/c2ba89131eea791d1b25205bdfdc86699767e2b88dee2a590b1a6caa51737deac8bad0260a5ded2f7c074b7db2f3a626bcf1fcf3cdf35974cbeea5e2e6764f60 + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -9549,7 +10499,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.20.0": +"rollup@npm:^4.20.0, rollup@npm:^4.24.0": version: 4.26.0 resolution: "rollup@npm:4.26.0" dependencies: @@ -9956,6 +10906,15 @@ __metadata: languageName: node linkType: hard +"smoldot@npm:2.0.31": + version: 2.0.31 + resolution: "smoldot@npm:2.0.31" + dependencies: + ws: "npm:^8.8.1" + checksum: 10c0/610bf1ae1a80e7f682af144e2d0088e4d49949089c7b8bc29961ec112e120af78b7bf181718ad5f923cfca69d6b5ce23894ab10872dac6595ca1554277606977 + languageName: node + linkType: hard + "snake-case@npm:^3.0.4": version: 3.0.4 resolution: "snake-case@npm:3.0.4" @@ -10018,6 +10977,15 @@ __metadata: languageName: node linkType: hard +"sort-keys@npm:^5.0.0": + version: 5.1.0 + resolution: "sort-keys@npm:5.1.0" + dependencies: + is-plain-obj: "npm:^4.0.0" + checksum: 10c0/fdb7aeb02368ad91b2ea947b59f3c95d80f8c71bbcb5741ebd55852994f54a129af3b3663b280951566fe5897de056428810dbb58c61db831e588c0ac110f2b0 + languageName: node + linkType: hard + "source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" @@ -10032,6 +11000,15 @@ __metadata: languageName: node linkType: hard +"source-map@npm:0.8.0-beta.0": + version: 0.8.0-beta.0 + resolution: "source-map@npm:0.8.0-beta.0" + dependencies: + whatwg-url: "npm:^7.0.0" + checksum: 10c0/fb4d9bde9a9fdb2c29b10e5eae6c71d10e09ef467e1afb75fdec2eb7e11fa5b343a2af553f74f18b695dbc0b81f9da2e9fa3d7a317d5985e9939499ec6087835 + languageName: node + linkType: hard + "source-map@npm:^0.7.4": version: 0.7.4 resolution: "source-map@npm:0.7.4" @@ -10039,6 +11016,40 @@ __metadata: languageName: node linkType: hard +"spdx-correct@npm:^3.0.0": + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" + dependencies: + spdx-expression-parse: "npm:^3.0.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/49208f008618b9119208b0dadc9208a3a55053f4fd6a0ae8116861bd22696fc50f4142a35ebfdb389e05ccf2de8ad142573fefc9e26f670522d899f7b2fe7386 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.5.0 + resolution: "spdx-exceptions@npm:2.5.0" + checksum: 10c0/37217b7762ee0ea0d8b7d0c29fd48b7e4dfb94096b109d6255b589c561f57da93bf4e328c0290046115961b9209a8051ad9f525e48d433082fc79f496a4ea940 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: "npm:^2.1.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/6f8a41c87759fa184a58713b86c6a8b028250f158159f1d03ed9d1b6ee4d9eefdc74181c8ddc581a341aa971c3e7b79e30b59c23b05d2436d5de1c30bdef7171 + languageName: node + linkType: hard + +"spdx-license-ids@npm:^3.0.0": + version: 3.0.20 + resolution: "spdx-license-ids@npm:3.0.20" + checksum: 10c0/bdff7534fad6ef59be49becda1edc3fb7f5b3d6f296a715516ab9d972b8ad59af2c34b2003e01db8970d4c673d185ff696ba74c6b61d3bf327e2b3eac22c297c + languageName: node + linkType: hard + "split-on-first@npm:^1.0.0": version: 1.1.0 resolution: "split-on-first@npm:1.1.0" @@ -10120,6 +11131,13 @@ __metadata: languageName: node linkType: hard +"stdin-discarder@npm:^0.2.2": + version: 0.2.2 + resolution: "stdin-discarder@npm:0.2.2" + checksum: 10c0/c78375e82e956d7a64be6e63c809c7f058f5303efcaf62ea48350af072bacdb99c06cba39209b45a071c1acbd49116af30df1df9abb448df78a6005b72f10537 + languageName: node + linkType: hard + "stream-shift@npm:^1.0.2": version: 1.0.3 resolution: "stream-shift@npm:1.0.3" @@ -10156,6 +11174,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.11": version: 4.0.11 resolution: "string.prototype.matchall@npm:4.0.11" @@ -10247,7 +11276,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -10270,6 +11299,13 @@ __metadata: languageName: node linkType: hard +"strip-final-newline@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-final-newline@npm:4.0.0" + checksum: 10c0/b0cf2b62d597a1b0e3ebc42b88767f0a0d45601f89fd379a928a1812c8779440c81abba708082c946445af1d6b62d5f16e2a7cf4f30d9d6587b89425fae801ff + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -10321,6 +11357,24 @@ __metadata: languageName: node linkType: hard +"sucrase@npm:^3.35.0": + version: 3.35.0 + resolution: "sucrase@npm:3.35.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.2" + commander: "npm:^4.0.0" + glob: "npm:^10.3.10" + lines-and-columns: "npm:^1.1.6" + mz: "npm:^2.7.0" + pirates: "npm:^4.0.1" + ts-interface-checker: "npm:^0.1.9" + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: 10c0/ac85f3359d2c2ecbf5febca6a24ae9bf96c931f05fde533c22a94f59c6a74895e5d5f0e871878dfd59c2697a75ebb04e4b2224ef0bfc24ca1210735c2ec191ef + languageName: node + linkType: hard + "superstruct@npm:^1.0.3": version: 1.0.4 resolution: "superstruct@npm:1.0.4" @@ -10403,6 +11457,24 @@ __metadata: languageName: node linkType: hard +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + "thread-stream@npm:^0.15.1": version: 0.15.2 resolution: "thread-stream@npm:0.15.2" @@ -10426,6 +11498,23 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^0.3.1": + version: 0.3.1 + resolution: "tinyexec@npm:0.3.1" + checksum: 10c0/11e7a7c5d8b3bddf8b5cbe82a9290d70a6fad84d528421d5d18297f165723cb53d2e737d8f58dcce5ca56f2e4aa2d060f02510b1f8971784f97eb3e9aec28f09 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.9": + version: 0.2.10 + resolution: "tinyglobby@npm:0.2.10" + dependencies: + fdir: "npm:^6.4.2" + picomatch: "npm:^4.0.2" + checksum: 10c0/ce946135d39b8c0e394e488ad59f4092e8c4ecd675ef1bcd4585c47de1b325e61ec6adfbfbe20c3c2bfa6fd674c5b06de2a2e65c433f752ae170aff11793e5ef + languageName: node + linkType: hard + "tinypool@npm:^0.8.3": version: 0.8.4 resolution: "tinypool@npm:0.8.4" @@ -10456,6 +11545,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^1.0.1": + version: 1.0.1 + resolution: "tr46@npm:1.0.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10c0/41525c2ccce86e3ef30af6fa5e1464e6d8bb4286a58ea8db09228f598889581ef62347153f6636cd41553dc41685bdfad0a9d032ef58df9fbb0792b3447d0f04 + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -10463,6 +11561,15 @@ __metadata: languageName: node linkType: hard +"tree-kill@npm:^1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 10c0/7b1b7c7f17608a8f8d20a162e7957ac1ef6cd1636db1aba92f4e072dc31818c2ff0efac1e3d91064ede67ed5dc57c565420531a8134090a12ac10cf792ab14d2 + languageName: node + linkType: hard + "ts-api-utils@npm:^1.3.0": version: 1.4.0 resolution: "ts-api-utils@npm:1.4.0" @@ -10472,6 +11579,13 @@ __metadata: languageName: node linkType: hard +"ts-interface-checker@npm:^0.1.9": + version: 0.1.13 + resolution: "ts-interface-checker@npm:0.1.13" + checksum: 10c0/232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7 + languageName: node + linkType: hard + "ts-invariant@npm:^0.10.3": version: 0.10.3 resolution: "ts-invariant@npm:0.10.3" @@ -10481,6 +11595,15 @@ __metadata: languageName: node linkType: hard +"tsc-prog@npm:^2.3.0": + version: 2.3.0 + resolution: "tsc-prog@npm:2.3.0" + peerDependencies: + typescript: ">=4" + checksum: 10c0/ca0ee722557e7974a221d6b3fa28dcbcc5e98b7bce9402bf113eae7c835d59644d24b48ac65d15c7f8dbe8cab61c54c4b0b2d252212c72bc4d09ce1fe8fbc937 + languageName: node + linkType: hard + "tsconfck@npm:^3.0.3": version: 3.1.4 resolution: "tsconfck@npm:3.1.4" @@ -10528,6 +11651,47 @@ __metadata: languageName: node linkType: hard +"tsup@npm:^8.2.4": + version: 8.3.5 + resolution: "tsup@npm:8.3.5" + dependencies: + bundle-require: "npm:^5.0.0" + cac: "npm:^6.7.14" + chokidar: "npm:^4.0.1" + consola: "npm:^3.2.3" + debug: "npm:^4.3.7" + esbuild: "npm:^0.24.0" + joycon: "npm:^3.1.1" + picocolors: "npm:^1.1.1" + postcss-load-config: "npm:^6.0.1" + resolve-from: "npm:^5.0.0" + rollup: "npm:^4.24.0" + source-map: "npm:0.8.0-beta.0" + sucrase: "npm:^3.35.0" + tinyexec: "npm:^0.3.1" + tinyglobby: "npm:^0.2.9" + tree-kill: "npm:^1.2.2" + peerDependencies: + "@microsoft/api-extractor": ^7.36.0 + "@swc/core": ^1 + postcss: ^8.4.12 + typescript: ">=4.5.0" + peerDependenciesMeta: + "@microsoft/api-extractor": + optional: true + "@swc/core": + optional: true + postcss: + optional: true + typescript: + optional: true + bin: + tsup: dist/cli-default.js + tsup-node: dist/cli-node.js + checksum: 10c0/7794953cbc784b7c8f14c4898d36a293b815b528d3098c2416aeaa2b4775dc477132cd12f75f6d32737dfd15ba10139c73f7039045352f2ba1ea7e5fe6fe3773 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -10558,6 +11722,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^4.23.0, type-fest@npm:^4.6.0, type-fest@npm:^4.7.1": + version: 4.26.1 + resolution: "type-fest@npm:4.26.1" + checksum: 10c0/d2719ff8d380befe8a3c61068f37f28d6fa2849fd140c5d2f0f143099e371da6856aad7c97e56b83329d45bfe504afe9fd936a7cff600cc0d46aa9ffb008d6c6 + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-buffer@npm:1.0.2" @@ -10610,7 +11781,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.4.3": +"typescript@npm:^5.4.3, typescript@npm:^5.5.4": version: 5.6.3 resolution: "typescript@npm:5.6.3" bin: @@ -10620,7 +11791,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.4.3#optional!builtin": +"typescript@patch:typescript@npm%3A^5.4.3#optional!builtin, typescript@patch:typescript@npm%3A^5.5.4#optional!builtin": version: 5.6.3 resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=5adc0c" bin: @@ -10714,6 +11885,20 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 10c0/e4ed0de05b0a05e735c7d8a2930881e5efcfc3ec897204d5d33e7e6247f4c31eac92e383a15d9a6bccb7319b4271ee4bea946e211bf14951fec6ff2cbbb66a92 + languageName: node + linkType: hard + +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -10916,6 +12101,16 @@ __metadata: languageName: node linkType: hard +"validate-npm-package-license@npm:^3.0.4": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: "npm:^3.0.0" + spdx-expression-parse: "npm:^3.0.0" + checksum: 10c0/7b91e455a8de9a0beaa9fe961e536b677da7f48c9a493edf4d4d4a87fd80a7a10267d438723364e432c2fcd00b5650b5378275cded362383ef570276e6312f4f + languageName: node + linkType: hard + "valtio@npm:1.11.2": version: 1.11.2 resolution: "valtio@npm:1.11.2" @@ -11307,6 +12502,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^4.0.2": + version: 4.0.2 + resolution: "webidl-conversions@npm:4.0.2" + checksum: 10c0/def5c5ac3479286dffcb604547628b2e6b46c5c5b8a8cfaa8c71dc3bafc85859bde5fbe89467ff861f571ab38987cf6ab3d6e7c80b39b999e50e803c12f3164f + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" @@ -11317,6 +12519,17 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^7.0.0": + version: 7.1.0 + resolution: "whatwg-url@npm:7.1.0" + dependencies: + lodash.sortby: "npm:^4.7.0" + tr46: "npm:^1.0.1" + webidl-conversions: "npm:^4.0.2" + checksum: 10c0/2785fe4647690e5a0225a79509ba5e21fdf4a71f9de3eabdba1192483fe006fc79961198e0b99f82751557309f17fc5a07d4d83c251aa5b2f85ba71e674cbee9 + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.0.2": version: 1.0.2 resolution: "which-boxed-primitive@npm:1.0.2" @@ -11463,6 +12676,41 @@ __metadata: languageName: node linkType: hard +"write-file-atomic@npm:^5.0.1": + version: 5.0.1 + resolution: "write-file-atomic@npm:5.0.1" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^4.0.1" + checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d + languageName: node + linkType: hard + +"write-json-file@npm:^6.0.0": + version: 6.0.0 + resolution: "write-json-file@npm:6.0.0" + dependencies: + detect-indent: "npm:^7.0.1" + is-plain-obj: "npm:^4.1.0" + sort-keys: "npm:^5.0.0" + write-file-atomic: "npm:^5.0.1" + checksum: 10c0/3f8f0caec7948d696b1e898512547bba7b63e99eb5b67ddb74cd9ea02aa0b88d48f5b710be3b26ac3ffa8c3e58c779bd610fb349d3cec6e8fb621596a8f85069 + languageName: node + linkType: hard + +"write-package@npm:^7.1.0": + version: 7.1.0 + resolution: "write-package@npm:7.1.0" + dependencies: + deepmerge-ts: "npm:^7.1.0" + read-pkg: "npm:^9.0.1" + sort-keys: "npm:^5.0.0" + type-fest: "npm:^4.23.0" + write-json-file: "npm:^6.0.0" + checksum: 10c0/0db9ca588fc00c753409633ce7777eae4147c6b60993643399f4af0e0ce672b5943587ff5269c3adda36090065d0ef1cf2c6936a62e0d1a548dc81c6507f4f2a + languageName: node + linkType: hard + "ws@npm:8.18.0, ws@npm:^8.15.1, ws@npm:^8.18.0, ws@npm:^8.8.1": version: 8.18.0 resolution: "ws@npm:8.18.0" @@ -11629,6 +12877,13 @@ __metadata: languageName: node linkType: hard +"yoctocolors@npm:^2.0.0": + version: 2.1.1 + resolution: "yoctocolors@npm:2.1.1" + checksum: 10c0/85903f7fa96f1c70badee94789fade709f9d83dab2ec92753d612d84fcea6d34c772337a9f8914c6bed2f5fc03a428ac5d893e76fab636da5f1236ab725486d0 + languageName: node + linkType: hard + "zen-observable-ts@npm:^1.2.5": version: 1.2.5 resolution: "zen-observable-ts@npm:1.2.5" From 65bf7ad55abde39ffd4d6a30e06584a4caaf855e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 14 Nov 2024 19:12:31 +0700 Subject: [PATCH 02/84] expose untyped api --- packages/app/src/contexts/Api/index.tsx | 9 ++++++++- packages/app/src/model/Api/index.ts | 12 +++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 8ffa019c9..dd9c2c87e 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -226,7 +226,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { SubscriptionsController.set(network, 'activeEra', new ActiveEra(network)); }; - const handlePapiReady = (e: Event) => { + const handlePapiReady = async (e: Event) => { if (isCustomEvent(e)) { const { chainType, genesisHash, ss58Format, tokenDecimals, tokenSymbol } = e.detail; @@ -241,7 +241,14 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { }; setChainSpecs({ ...newChainSpecs, received: true }); + const apiInstance = ApiController.get(network); + const pApi = apiInstance.papiApi; + // TODO: set consts from here. + console.log( + 'bonding duration: ', + await pApi.constants.Staking.BondingDuration() + ); } } }; diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index e4dee3a01..642c421a4 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -16,7 +16,7 @@ import { SubscriptionsController } from 'controllers/Subscriptions'; import { ScProvider } from '@polkadot/rpc-provider/substrate-connect'; import { WellKnownChain } from '@substrate/connect'; import * as Sc from '@substrate/connect'; -import type { PolkadotClient } from 'polkadot-api'; +import type { PolkadotClient, UnsafeApi } from 'polkadot-api'; import { createClient } from 'polkadot-api'; import { getWsProvider } from 'polkadot-api/ws-provider/web'; import { getSmProvider } from 'polkadot-api/sm-provider'; @@ -37,6 +37,9 @@ export class Api { // PAPI Instance. #papiClient: PolkadotClient; + // PAPI API. + #papiApi: UnsafeApi; + // PAPI Chain Spec. #papiChainSpec: PapiChainSpec; @@ -57,6 +60,10 @@ export class Api { return this.#papiClient; } + get papiApi() { + return this.#papiApi; + } + get papiChainSpec() { return this.#papiChainSpec; } @@ -104,6 +111,9 @@ export class Api { // Wait for api to be ready. await this.#api.isReady; + // Initialise PAPI API. + this.#papiApi = this.#papiClient.getUnsafeApi(); + // Fetch chain spec and metadata from PAPI client. await this.fetchChainSpec(); } catch (e) { From e3f2cbc93ab0b5eb17a93ced7e4e403fa67f179f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 14 Nov 2024 19:19:22 +0700 Subject: [PATCH 03/84] destroy on disconnect --- packages/app/src/contexts/Api/index.tsx | 2 +- packages/app/src/model/Api/index.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index dd9c2c87e..de563126d 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -486,7 +486,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { reInitialiseApi(connectionType); }, [network]); - // Call `unsubscribe` on active instnace on unmount. + // Call `unsubscribe` on active instance on unmount. useEffect( () => () => { const instance = ApiController.get(network); diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index 642c421a4..b95319024 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -280,6 +280,9 @@ export class Api { await this.#provider?.disconnect(); await this.#api?.disconnect(); + // Disconnect from PAPI Client. + this.#papiClient?.destroy(); + // Tell UI Api is destroyed. if (destroy) { // NOTE: destroyed event is not currently in use. From 2eb64547d5dd544c34fdd2a5e0fdcba2a71bfb2e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 09:22:24 +0700 Subject: [PATCH 04/84] consts to papi --- packages/app/src/contexts/Api/index.tsx | 70 +++++++++++++---- packages/app/src/model/Api/index.ts | 21 +++++ .../src/model/Query/StakingConstants/index.ts | 76 ------------------- 3 files changed, 78 insertions(+), 89 deletions(-) delete mode 100644 packages/app/src/model/Query/StakingConstants/index.ts diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index de563126d..4b3de42b3 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -37,7 +37,6 @@ import type { ApiStatus, ConnectionType, } from 'model/Api/types'; -import { StakingConstants } from 'model/Query/StakingConstants'; import { Era } from 'model/Query/Era'; import { NetworkMeta } from 'model/Query/NetworkMeta'; import { SubscriptionsController } from 'controllers/Subscriptions'; @@ -169,9 +168,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // 1. Fetch network data for bootstrapping app state: - // Get general network constants for staking UI. - const newConsts = await new StakingConstants().fetch(api, network); - // Get active and previous era. const { activeEra: newActiveEra, previousEra } = await new Era().fetch(api); @@ -184,7 +180,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // 2. Populate all config state: - setConsts(newConsts); setStateWithRef(newNetworkMetrics, setNetworkMetrics, networkMetricsRef); const { index, start } = newActiveEra; setStateWithRef({ index, start }, setActiveEra, activeEraRef); @@ -239,16 +234,65 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { tokenSymbol, received: true, }; - setChainSpecs({ ...newChainSpecs, received: true }); - const apiInstance = ApiController.get(network); - const pApi = apiInstance.papiApi; - - // TODO: set consts from here. - console.log( - 'bonding duration: ', - await pApi.constants.Staking.BondingDuration() + const api = ApiController.get(network); + const bondingDuration = await api.getConstant( + 'Staking', + 'BondingDuration', + 0 + ); + const sessionsPerEra = await api.getConstant( + 'Staking', + 'SessionsPerEra', + 0 + ); + const maxExposurePageSize = await api.getConstant( + 'Staking', + 'MaxExposurePageSize', + 0 + ); + const historyDepth = await api.getConstant( + 'Staking', + 'HistoryDepth', + 0 + ); + const expectedBlockTime = await api.getConstant( + 'Babe', + 'ExpectedBlockTime', + 0 ); + const epochDuration = await api.getConstant('Babe', 'EpochDuration', 0); + const existentialDeposit = await api.getConstant( + 'Balances', + 'ExistentialDeposit', + 0 + ); + const fastUnstakeDeposit = await api.getConstant( + 'FastUnstake', + 'Deposit', + 0 + ); + const poolsPalletId = await api.getConstant( + 'NominationPools', + 'PalletId', + new Uint8Array(0), + 'asBytes' + ); + + setChainSpecs({ ...newChainSpecs, received: true }); + + setConsts({ + maxNominations: new BigNumber(16), + bondDuration: new BigNumber(bondingDuration), + sessionsPerEra: new BigNumber(sessionsPerEra), + maxExposurePageSize: new BigNumber(maxExposurePageSize), + historyDepth: new BigNumber(historyDepth), + expectedBlockTime: new BigNumber(expectedBlockTime.toString()), + epochDuration: new BigNumber(epochDuration.toString()), + existentialDeposit: new BigNumber(existentialDeposit.toString()), + fastUnstakeDeposit: new BigNumber(fastUnstakeDeposit.toString()), + poolsPalletId, + }); } } }; diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index b95319024..b0018d5fa 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -196,6 +196,27 @@ export class Api { } } + // Get a pallet constant, with a fallback value. + getConstant = async ( + pallet: string, + key: string, + fallback: T, + formatter?: 'asBytes' + ): Promise => { + try { + const result = await this.#papiApi.constants[pallet][key](); + + switch (formatter) { + case 'asBytes': + return result.asBytes(); + default: + return result; + } + } catch (e) { + return fallback; + } + }; + // Handler for dispatching `papi-ready` events. dispatchPapiReadyEvent() { const detail: PapiReadyEvent = { diff --git a/packages/app/src/model/Query/StakingConstants/index.ts b/packages/app/src/model/Query/StakingConstants/index.ts deleted file mode 100644 index e68ff841e..000000000 --- a/packages/app/src/model/Query/StakingConstants/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { ApiPromise } from '@polkadot/api'; -import BigNumber from 'bignumber.js'; -import { NetworkList } from 'config/networks'; -import { stringToBn } from 'library/Utils'; - -export class StakingConstants { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - - // Network config fallback values. - FALLBACK = { - MAX_NOMINATIONS: new BigNumber(16), - BONDING_DURATION: new BigNumber(28), - SESSIONS_PER_ERA: new BigNumber(6), - MAX_ELECTING_VOTERS: new BigNumber(22500), - EXPECTED_BLOCK_TIME: new BigNumber(6000), - EPOCH_DURATION: new BigNumber(2400), - }; - - // Fetch network constants. - async fetch(api: ApiPromise, network: string) { - const allPromises = [ - api.consts.staking.bondingDuration, - api.consts.staking.maxNominations, - api.consts.staking.sessionsPerEra, - api.consts.electionProviderMultiPhase.maxElectingVoters, - api.consts.babe.expectedBlockTime, - api.consts.babe.epochDuration, - api.consts.balances.existentialDeposit, - api.consts.staking.historyDepth, - api.consts.fastUnstake.deposit, - api.consts.nominationPools.palletId, - api.consts.staking.maxExposurePageSize, - ]; - - const consts = await Promise.all(allPromises); - - return { - bondDuration: consts[0] - ? stringToBn(consts[0].toString()) - : this.FALLBACK.BONDING_DURATION, - maxNominations: consts[1] - ? stringToBn(consts[1].toString()) - : this.FALLBACK.MAX_NOMINATIONS, - sessionsPerEra: consts[2] - ? stringToBn(consts[2].toString()) - : this.FALLBACK.SESSIONS_PER_ERA, - maxElectingVoters: consts[3] - ? stringToBn(consts[3].toString()) - : this.FALLBACK.MAX_ELECTING_VOTERS, - expectedBlockTime: consts[4] - ? stringToBn(consts[4].toString()) - : this.FALLBACK.EXPECTED_BLOCK_TIME, - epochDuration: consts[5] - ? stringToBn(consts[5].toString()) - : this.FALLBACK.EPOCH_DURATION, - existentialDeposit: consts[6] - ? stringToBn(consts[6].toString()) - : new BigNumber(0), - historyDepth: consts[7] - ? stringToBn(consts[7].toString()) - : new BigNumber(0), - fastUnstakeDeposit: consts[8] - ? stringToBn(consts[8].toString()) - : new BigNumber(0), - poolsPalletId: consts[9] ? consts[9].toU8a() : new Uint8Array(0), - maxExposurePageSize: consts[10] - ? stringToBn(consts[10].toString()) - : NetworkList[network].maxExposurePageSize, - }; - } -} From f64fb6c4f45719ec355e9331458ae8607ef4fb73 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 09:42:42 +0700 Subject: [PATCH 05/84] activeEra to papi --- packages/app/src/contexts/Api/index.tsx | 5 ++++- packages/app/src/model/Api/index.ts | 5 +++-- packages/app/src/model/Api/types.ts | 3 +++ packages/app/src/model/Query/Era/index.ts | 26 ++++++++++++++--------- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 4b3de42b3..4b0147ce5 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -165,11 +165,14 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { const bootstrapNetworkConfig = async () => { const apiInstance = ApiController.get(network); const api = apiInstance.api; + const pApi = apiInstance.papiApi; // 1. Fetch network data for bootstrapping app state: // Get active and previous era. - const { activeEra: newActiveEra, previousEra } = await new Era().fetch(api); + const { activeEra: newActiveEra, previousEra } = await new Era().fetch( + pApi + ); // Get network meta data related to staking and pools. const { diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index b0018d5fa..4b3ac6a0d 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -9,6 +9,7 @@ import type { APIEventDetail, ConnectionType, EventApiStatus, + PapiApi, PapiChainSpec, PapiReadyEvent, } from './types'; @@ -16,7 +17,7 @@ import { SubscriptionsController } from 'controllers/Subscriptions'; import { ScProvider } from '@polkadot/rpc-provider/substrate-connect'; import { WellKnownChain } from '@substrate/connect'; import * as Sc from '@substrate/connect'; -import type { PolkadotClient, UnsafeApi } from 'polkadot-api'; +import type { PolkadotClient } from 'polkadot-api'; import { createClient } from 'polkadot-api'; import { getWsProvider } from 'polkadot-api/ws-provider/web'; import { getSmProvider } from 'polkadot-api/sm-provider'; @@ -38,7 +39,7 @@ export class Api { #papiClient: PolkadotClient; // PAPI API. - #papiApi: UnsafeApi; + #papiApi: PapiApi; // PAPI Chain Spec. #papiChainSpec: PapiChainSpec; diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index eebef4ff4..eaae0bf6f 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -1,6 +1,7 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import type { UnsafeApi } from 'polkadot-api'; import type { NetworkName, SystemChainId } from 'types'; export interface APIConfig { @@ -28,6 +29,8 @@ export type PapiReadyEvent = PapiChainSpec & { chainType: string; }; +export type PapiApi = UnsafeApi; + export type ConnectionType = 'ws' | 'sc'; export type ApiStatus = 'connecting' | 'connected' | 'disconnected' | 'ready'; diff --git a/packages/app/src/model/Query/Era/index.ts b/packages/app/src/model/Query/Era/index.ts index 875984527..fb83230c7 100644 --- a/packages/app/src/model/Query/Era/index.ts +++ b/packages/app/src/model/Query/Era/index.ts @@ -1,21 +1,27 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ApiPromise } from '@polkadot/api'; import BigNumber from 'bignumber.js'; -import type { AnyApi } from 'types'; +import type { PapiApi } from 'model/Api/types'; export class Era { // Fetch network constants. - async fetch(api: ApiPromise) { - // Fetch the active era. - const result = JSON.parse( - ((await api.query.staking.activeEra()) as AnyApi) - .unwrapOrDefault() - .toString() - ); + async fetch(api: PapiApi) { + let result; + try { + const { index, start } = await api.query.Staking.ActiveEra.getValue(); + result = { + start, + index, + }; + } catch (e) { + result = { + start: 0, + index: 0, + }; + } - // Store active era. + // Store active era as BigNumbers. const activeEra = { index: new BigNumber(result.index), start: new BigNumber(result.start), From 6e2e58fdb2894e62846cc16feec476bef243cecf Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:03:32 +0700 Subject: [PATCH 06/84] NetworkMeta fetch to pApi --- packages/app/src/contexts/Api/defaults.ts | 1 - packages/app/src/contexts/Api/index.tsx | 3 +- packages/app/src/contexts/Api/types.ts | 1 - .../app/src/model/Query/NetworkMeta/index.ts | 150 ++++++++++-------- .../model/Subscribe/StakingMetrics/index.ts | 18 +-- .../src/pages/Overview/NetworkSats/index.tsx | 4 +- 6 files changed, 92 insertions(+), 85 deletions(-) diff --git a/packages/app/src/contexts/Api/defaults.ts b/packages/app/src/contexts/Api/defaults.ts index 8d647590e..40ebace65 100644 --- a/packages/app/src/contexts/Api/defaults.ts +++ b/packages/app/src/contexts/Api/defaults.ts @@ -71,7 +71,6 @@ export const defaultPoolsConfig: APIPoolsConfig = { }; export const defaultStakingMetrics: APIStakingMetrics = { - totalNominators: new BigNumber(0), totalValidators: new BigNumber(0), lastReward: new BigNumber(0), lastTotalStake: new BigNumber(0), diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 4b0147ce5..1931cab23 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -164,7 +164,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // Bootstrap app-wide chain state. const bootstrapNetworkConfig = async () => { const apiInstance = ApiController.get(network); - const api = apiInstance.api; const pApi = apiInstance.papiApi; // 1. Fetch network data for bootstrapping app state: @@ -179,7 +178,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { networkMetrics: newNetworkMetrics, poolsConfig: newPoolsConfig, stakingMetrics: newStakingMetrics, - } = await new NetworkMeta().fetch(api, newActiveEra, previousEra); + } = await new NetworkMeta().fetch(pApi, newActiveEra, previousEra); // 2. Populate all config state: diff --git a/packages/app/src/contexts/Api/types.ts b/packages/app/src/contexts/Api/types.ts index 0420ed7f8..5dd28bc86 100644 --- a/packages/app/src/contexts/Api/types.ts +++ b/packages/app/src/contexts/Api/types.ts @@ -62,7 +62,6 @@ export interface APIPoolsConfig { } export interface APIStakingMetrics { - totalNominators: BigNumber; totalValidators: BigNumber; lastReward: BigNumber; lastTotalStake: BigNumber; diff --git a/packages/app/src/model/Query/NetworkMeta/index.ts b/packages/app/src/model/Query/NetworkMeta/index.ts index ae8839fff..b4e14211b 100644 --- a/packages/app/src/model/Query/NetworkMeta/index.ts +++ b/packages/app/src/model/Query/NetworkMeta/index.ts @@ -1,97 +1,109 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { ApiPromise } from '@polkadot/api'; -import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import type { APIActiveEra } from 'contexts/Api/types'; import { stringToBn } from 'library/Utils'; +import type { PapiApi } from 'model/Api/types'; export class NetworkMeta { // Fetch network constants. - async fetch( - api: ApiPromise, - activeEra: APIActiveEra, - previousEra: BigNumber - ) { - // Fetch network configuration. - const networkMeta = await api.queryMulti([ - // Network metrics. - api.query.balances.totalIssuance, - api.query.auctions.auctionCounter, - api.query.paraSessionInfo.earliestStoredSession, - api.query.fastUnstake.erasToCheckPerBlock, - api.query.staking.minimumActiveStake, - // Nomination pool configs. - api.query.nominationPools.counterForPoolMembers, - api.query.nominationPools.counterForBondedPools, - api.query.nominationPools.counterForRewardPools, - api.query.nominationPools.lastPoolId, - api.query.nominationPools.maxPoolMembers, - api.query.nominationPools.maxPoolMembersPerPool, - api.query.nominationPools.maxPools, - api.query.nominationPools.minCreateBond, - api.query.nominationPools.minJoinBond, - api.query.nominationPools.globalMaxCommission, - // Staking metrics. - api.query.staking.counterForNominators, - api.query.staking.counterForValidators, - api.query.staking.maxValidatorsCount, - api.query.staking.validatorCount, - [api.query.staking.erasValidatorReward, previousEra.toString()], - [api.query.staking.erasTotalStake, previousEra.toString()], - api.query.staking.minNominatorBond, - [api.query.staking.erasTotalStake, activeEra.index.toString()], - api.query.staking.counterForNominators, + async fetch(pApi: PapiApi, activeEra: APIActiveEra, previousEra: BigNumber) { + const totalIssuance = await pApi.query.Balances.TotalIssuance.getValue(); + + const [ + auctionCounter, + earliestStoredSession, + erasToCheckPerBlock, + minimumActiveStake, + counterForPoolMembers, + counterForBondedPools, + counterForRewardPools, + lastPoolId, + maxPoolMembersRaw, + maxPoolMembersPerPoolRaw, + maxPoolsRaw, + minCreateBond, + minJoinBond, + globalMaxCommission, + counterForNominators, + counterForValidators, + maxValidatorsCount, + validatorCount, + prevErasValidatorReward, + prevEraErasTotalStake, + minNominatorBond, + activeEraErasTotalStake, + ] = await Promise.all([ + pApi.query.Auctions.AuctionCounter.getValue(), + pApi.query.ParaSessionInfo.EarliestStoredSession.getValue(), + pApi.query.FastUnstake.ErasToCheckPerBlock.getValue(), + pApi.query.Staking.MinimumActiveStake.getValue(), + pApi.query.NominationPools.CounterForPoolMembers.getValue(), + pApi.query.NominationPools.CounterForBondedPools.getValue(), + pApi.query.NominationPools.CounterForRewardPools.getValue(), + pApi.query.NominationPools.LastPoolId.getValue(), + pApi.query.NominationPools.MaxPoolMembers.getValue(), + pApi.query.NominationPools.MaxPoolMembersPerPool.getValue(), + pApi.query.NominationPools.MaxPools.getValue(), + pApi.query.NominationPools.MinCreateBond.getValue(), + pApi.query.NominationPools.MinJoinBond.getValue(), + pApi.query.NominationPools.GlobalMaxCommission.getValue(), + pApi.query.Staking.CounterForNominators.getValue(), + pApi.query.Staking.CounterForValidators.getValue(), + pApi.query.Staking.MaxValidatorsCount.getValue(), + pApi.query.Staking.ValidatorCount.getValue(), + pApi.query.Staking.ErasValidatorReward.getValue(previousEra.toString()), + pApi.query.Staking.ErasTotalStake.getValue(previousEra.toString()), + pApi.query.Staking.MinNominatorBond.getValue(), + pApi.query.Staking.ErasTotalStake.getValue(activeEra.index.toString()), ]); - // format optional configs to BigNumber or null. - const maxPoolMembers = networkMeta[9].toHuman() - ? new BigNumber(rmCommas(networkMeta[9].toString())) - : null; + // Format globalMaxCommission from a perbill to a percent. + const globalMaxCommissionAsPercent = BigInt(globalMaxCommission) / 1000000n; - const maxPoolMembersPerPool = networkMeta[10].toHuman() - ? new BigNumber(rmCommas(networkMeta[10].toString())) + // Format max pool members to be a BigNumber, or null if it's not set. + const maxPoolMembers = maxPoolMembersRaw + ? new BigNumber(maxPoolMembersRaw.toString()) : null; - const maxPools = networkMeta[11].toHuman() - ? new BigNumber(rmCommas(networkMeta[11].toString())) + // Format max pool members per pool to be a BigNumber, or null if it's not set. + const maxPoolMembersPerPool = maxPoolMembersPerPoolRaw + ? new BigNumber(maxPoolMembersPerPoolRaw.toString()) : null; + // Format max pools to be a BigNumber, or null if it's not set. + const maxPools = maxPoolsRaw ? new BigNumber(maxPoolsRaw.toString()) : null; + return { networkMetrics: { - totalIssuance: new BigNumber(networkMeta[0].toString()), - auctionCounter: new BigNumber(networkMeta[1].toString()), - earliestStoredSession: new BigNumber(networkMeta[2].toString()), - fastUnstakeErasToCheckPerBlock: Number( - rmCommas(networkMeta[3].toString()) - ), - minimumActiveStake: new BigNumber(networkMeta[4].toString()), + totalIssuance: new BigNumber(totalIssuance.toString()), + auctionCounter: new BigNumber(auctionCounter.toString()), + earliestStoredSession: new BigNumber(earliestStoredSession.toString()), + fastUnstakeErasToCheckPerBlock: Number(erasToCheckPerBlock.toString()), + minimumActiveStake: new BigNumber(minimumActiveStake.toString()), }, poolsConfig: { - counterForPoolMembers: stringToBn(networkMeta[5].toString()), - counterForBondedPools: stringToBn(networkMeta[6].toString()), - counterForRewardPools: stringToBn(networkMeta[7].toString()), - lastPoolId: stringToBn(networkMeta[8].toString()), + counterForPoolMembers: stringToBn(counterForPoolMembers.toString()), + counterForBondedPools: stringToBn(counterForBondedPools.toString()), + counterForRewardPools: stringToBn(counterForRewardPools.toString()), + lastPoolId: stringToBn(lastPoolId.toString()), maxPoolMembers, maxPoolMembersPerPool, maxPools, - minCreateBond: stringToBn(networkMeta[12].toString()), - minJoinBond: stringToBn(networkMeta[13].toString()), - globalMaxCommission: Number( - String(networkMeta[14]?.toHuman() || '100%').slice(0, -1) - ), + minCreateBond: stringToBn(minCreateBond.toString()), + minJoinBond: stringToBn(minJoinBond.toString()), + globalMaxCommission: Number(globalMaxCommissionAsPercent.toString()), }, stakingMetrics: { - totalNominators: stringToBn(networkMeta[15].toString()), - totalValidators: stringToBn(networkMeta[16].toString()), - maxValidatorsCount: stringToBn(networkMeta[17].toString()), - validatorCount: stringToBn(networkMeta[18].toString()), - lastReward: stringToBn(networkMeta[19].toString()), - lastTotalStake: stringToBn(networkMeta[20].toString()), - minNominatorBond: stringToBn(networkMeta[21].toString()), - totalStaked: stringToBn(networkMeta[22].toString()), - counterForNominators: stringToBn(networkMeta[23].toString()), + totalValidators: stringToBn(counterForValidators.toString()), + maxValidatorsCount: stringToBn(maxValidatorsCount.toString()), + validatorCount: stringToBn(validatorCount.toString()), + lastReward: stringToBn(prevErasValidatorReward.toString()), + lastTotalStake: stringToBn(prevEraErasTotalStake.toString()), + minNominatorBond: stringToBn(minNominatorBond.toString()), + totalStaked: stringToBn(activeEraErasTotalStake.toString()), + counterForNominators: stringToBn(counterForNominators.toString()), }, }; } diff --git a/packages/app/src/model/Subscribe/StakingMetrics/index.ts b/packages/app/src/model/Subscribe/StakingMetrics/index.ts index c7ac0c5c3..e0032237c 100644 --- a/packages/app/src/model/Subscribe/StakingMetrics/index.ts +++ b/packages/app/src/model/Subscribe/StakingMetrics/index.ts @@ -52,7 +52,6 @@ export class StakingMetrics implements Unsubscribable { if (api && this.#unsub === undefined) { const unsub = await api.queryMulti( [ - api.query.staking.counterForNominators, api.query.staking.counterForValidators, api.query.staking.maxValidatorsCount, api.query.staking.validatorCount, @@ -70,15 +69,14 @@ export class StakingMetrics implements Unsubscribable { ], (result) => { const stakingMetrics = { - totalNominators: stringToBn(result[0].toString()), - totalValidators: stringToBn(result[1].toString()), - maxValidatorsCount: stringToBn(result[2].toString()), - validatorCount: stringToBn(result[3].toString()), - lastReward: stringToBn(result[4].toString()), - lastTotalStake: stringToBn(result[5].toString()), - minNominatorBond: stringToBn(result[6].toString()), - totalStaked: stringToBn(result[7].toString()), - counterForNominators: stringToBn(result[8].toString()), + totalValidators: stringToBn(result[0].toString()), + maxValidatorsCount: stringToBn(result[1].toString()), + validatorCount: stringToBn(result[2].toString()), + lastReward: stringToBn(result[3].toString()), + lastTotalStake: stringToBn(result[4].toString()), + minNominatorBond: stringToBn(result[5].toString()), + totalStaked: stringToBn(result[6].toString()), + counterForNominators: stringToBn(result[7].toString()), }; document.dispatchEvent( diff --git a/packages/app/src/pages/Overview/NetworkSats/index.tsx b/packages/app/src/pages/Overview/NetworkSats/index.tsx index a6519210f..d21252ceb 100644 --- a/packages/app/src/pages/Overview/NetworkSats/index.tsx +++ b/packages/app/src/pages/Overview/NetworkSats/index.tsx @@ -15,7 +15,7 @@ export const NetworkStats = () => { const { t } = useTranslation('pages'); const { bondedPools } = useBondedPools(); const { getAverageRewardRate } = useAverageRewardRate(); - const { totalNominators, totalValidators } = useApi().stakingMetrics; + const { counterForNominators, totalValidators } = useApi().stakingMetrics; const { inflationToStakers } = getAverageRewardRate(false); @@ -27,7 +27,7 @@ export const NetworkStats = () => { }, { label: t('overview.totalNominators'), - value: totalNominators.toFormat(0), + value: counterForNominators.toFormat(0), helpKey: 'Total Nominators', }, { From cd397306182fc14b1e7d66b74a4c975fa4dd93a2 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:15:03 +0700 Subject: [PATCH 07/84] activeEra to `Subscription` --- packages/app/package.json | 1 + .../src/model/Subscribe/ActiveEra/index.ts | 105 +++++++----------- yarn.lock | 1 + 3 files changed, 44 insertions(+), 63 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index c4b9eae02..34c8de028 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -73,6 +73,7 @@ "xxhash-wasm": "^1.0.2" }, "devDependencies": { + "rxjs": "^7.8.1", "vite": "^5.2.12", "vite-bundle-visualizer": "^1.2.1", "vite-plugin-checker": "^0.7.0", diff --git a/packages/app/src/model/Subscribe/ActiveEra/index.ts b/packages/app/src/model/Subscribe/ActiveEra/index.ts index 79f3f4848..e60a334d0 100644 --- a/packages/app/src/model/Subscribe/ActiveEra/index.ts +++ b/packages/app/src/model/Subscribe/ActiveEra/index.ts @@ -1,34 +1,26 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import BigNumber from 'bignumber.js'; import { defaultActiveEra } from 'contexts/Api/defaults'; import type { APIActiveEra } from 'contexts/Api/types'; import { ApiController } from 'controllers/Api'; import { SubscriptionsController } from 'controllers/Subscriptions'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; -import type { AnyApi, NetworkName } from 'types'; +import type { NetworkName } from 'types'; import { StakingMetrics } from '../StakingMetrics'; +import type { Subscription } from 'rxjs'; export class ActiveEra implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; // Unsubscribe object. - #unsub: VoidFn; + #unsub: Subscription; // Store the active era. activeEra: APIActiveEra = defaultActiveEra; - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ - constructor(network: NetworkName) { this.#network = network; @@ -36,75 +28,62 @@ export class ActiveEra implements Unsubscribable { this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); + const { api, papiApi } = ApiController.get(this.#network); if (api && this.#unsub === undefined) { - const unsub = await api.query.staking.activeEra((result: AnyApi) => { - // determine activeEra: toString used as alternative to `toHuman`, that puts commas in - // numbers - const activeEra = JSON.parse(result.unwrapOrDefault().toString()); - - // Return early if errornous active era is returned. - if (activeEra.index === 0 || !activeEra.start) { - return; - } - - // Store active era. - this.activeEra = { - index: new BigNumber(activeEra.index), - start: new BigNumber(activeEra.start), - }; + // Testing the active era subscription. + const unsub = papiApi.query.Staking.ActiveEra.watchValue().subscribe( + (activeEra) => { + // Store active era. + this.activeEra = { + index: new BigNumber(activeEra.index.toString()), + start: new BigNumber(activeEra.start.toString()), + }; + + // Unsubscribe to staking metrics if it exists. + const subStakingMetrics = SubscriptionsController.get( + this.#network, + 'stakingMetrics' + ); - // Unsubscribe to staking metrics if it exists. - const subStakingMetrics = SubscriptionsController.get( - this.#network, - 'stakingMetrics' - ); - if (subStakingMetrics) { - subStakingMetrics.subscribe(); - SubscriptionsController.remove(this.#network, 'stakingMetrics'); - } + if (subStakingMetrics) { + subStakingMetrics.subscribe(); + SubscriptionsController.remove(this.#network, 'stakingMetrics'); + } - // Subscribe to staking metrics with new active era. - SubscriptionsController.set( - this.#network, - 'stakingMetrics', - new StakingMetrics( + // Subscribe to staking metrics with new active era. + SubscriptionsController.set( this.#network, - this.activeEra, - BigNumber.max(0, this.activeEra.index.minus(1)) - ) - ); - - document.dispatchEvent( - new CustomEvent('new-active-era', { - detail: { activeEra }, - }) - ); - }); + 'stakingMetrics', + new StakingMetrics( + this.#network, + this.activeEra, + BigNumber.max(0, this.activeEra.index.minus(1)) + ) + ); + + document.dispatchEvent( + new CustomEvent('new-active-era', { + detail: { activeEra }, + }) + ); + } + ); // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#unsub = unsub; } } catch (e) { // Block number subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#unsub?.unsubscribe === 'function') { + this.#unsub.unsubscribe(); } }; } diff --git a/yarn.lock b/yarn.lock index 7eee8079c..bd5810cd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4908,6 +4908,7 @@ __metadata: react-i18next: "npm:^15.0.2" react-router-dom: "npm:^6.23.1" react-scroll: "npm:^1.9.0" + rxjs: "npm:^7.8.1" styled-components: "npm:^6.1.13" styles: "workspace:*" ui-buttons: "workspace:*" From b6d019eba2e027e45a7e41883e98583de45c87f6 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:15:51 +0700 Subject: [PATCH 08/84] renames & fix --- packages/app/src/contexts/Api/index.tsx | 2 +- packages/app/src/model/Api/index.ts | 10 +++++----- packages/app/src/model/Subscribe/ActiveEra/index.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 1931cab23..75dbd271f 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -164,7 +164,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // Bootstrap app-wide chain state. const bootstrapNetworkConfig = async () => { const apiInstance = ApiController.get(network); - const pApi = apiInstance.papiApi; + const pApi = apiInstance.pApi; // 1. Fetch network data for bootstrapping app state: diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index 4b3ac6a0d..8fcf8f825 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -39,7 +39,7 @@ export class Api { #papiClient: PolkadotClient; // PAPI API. - #papiApi: PapiApi; + #pApi: PapiApi; // PAPI Chain Spec. #papiChainSpec: PapiChainSpec; @@ -61,8 +61,8 @@ export class Api { return this.#papiClient; } - get papiApi() { - return this.#papiApi; + get pApi() { + return this.#pApi; } get papiChainSpec() { @@ -113,7 +113,7 @@ export class Api { await this.#api.isReady; // Initialise PAPI API. - this.#papiApi = this.#papiClient.getUnsafeApi(); + this.#pApi = this.#papiClient.getUnsafeApi(); // Fetch chain spec and metadata from PAPI client. await this.fetchChainSpec(); @@ -205,7 +205,7 @@ export class Api { formatter?: 'asBytes' ): Promise => { try { - const result = await this.#papiApi.constants[pallet][key](); + const result = await this.#pApi.constants[pallet][key](); switch (formatter) { case 'asBytes': diff --git a/packages/app/src/model/Subscribe/ActiveEra/index.ts b/packages/app/src/model/Subscribe/ActiveEra/index.ts index e60a334d0..495344cdf 100644 --- a/packages/app/src/model/Subscribe/ActiveEra/index.ts +++ b/packages/app/src/model/Subscribe/ActiveEra/index.ts @@ -30,11 +30,11 @@ export class ActiveEra implements Unsubscribable { subscribe = async (): Promise => { try { - const { api, papiApi } = ApiController.get(this.#network); + const { pApi } = ApiController.get(this.#network); - if (api && this.#unsub === undefined) { + if (pApi && this.#unsub === undefined) { // Testing the active era subscription. - const unsub = papiApi.query.Staking.ActiveEra.watchValue().subscribe( + const unsub = pApi.query.Staking.ActiveEra.watchValue().subscribe( (activeEra) => { // Store active era. this.activeEra = { From 8f4984ecc72a4635717f812640470ce387fcc9be Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:20:04 +0700 Subject: [PATCH 09/84] `BlockNumber` to pApi, polish --- .../src/model/Subscribe/ActiveEra/index.ts | 20 ++++----- .../src/model/Subscribe/BlockNumber/index.ts | 41 +++++-------------- 2 files changed, 18 insertions(+), 43 deletions(-) diff --git a/packages/app/src/model/Subscribe/ActiveEra/index.ts b/packages/app/src/model/Subscribe/ActiveEra/index.ts index 495344cdf..9df6b011a 100644 --- a/packages/app/src/model/Subscribe/ActiveEra/index.ts +++ b/packages/app/src/model/Subscribe/ActiveEra/index.ts @@ -15,16 +15,14 @@ export class ActiveEra implements Unsubscribable { // The associated network for this instance. #network: NetworkName; - // Unsubscribe object. - #unsub: Subscription; + // Active subscription. + #sub: Subscription; // Store the active era. activeEra: APIActiveEra = defaultActiveEra; constructor(network: NetworkName) { this.#network = network; - - // Subscribe immediately. this.subscribe(); } @@ -32,9 +30,9 @@ export class ActiveEra implements Unsubscribable { try { const { pApi } = ApiController.get(this.#network); - if (pApi && this.#unsub === undefined) { + if (pApi && this.#sub === undefined) { // Testing the active era subscription. - const unsub = pApi.query.Staking.ActiveEra.watchValue().subscribe( + const sub = pApi.query.Staking.ActiveEra.watchValue().subscribe( (activeEra) => { // Store active era. this.activeEra = { @@ -71,19 +69,17 @@ export class ActiveEra implements Unsubscribable { ); } ); - - // Subscription now initialised. Store unsub. - this.#unsub = unsub; + this.#sub = sub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub?.unsubscribe === 'function') { - this.#unsub.unsubscribe(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } diff --git a/packages/app/src/model/Subscribe/BlockNumber/index.ts b/packages/app/src/model/Subscribe/BlockNumber/index.ts index 69a471116..43b93fbe0 100644 --- a/packages/app/src/model/Subscribe/BlockNumber/index.ts +++ b/packages/app/src/model/Subscribe/BlockNumber/index.ts @@ -1,47 +1,32 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; import type { NetworkName } from 'types'; export class BlockNumber implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; // The current block number. blockNumber = '0'; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor(network: NetworkName) { this.#network = network; - - // Subscribe immediately. this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); + const { pApi } = ApiController.get(this.#network); - if (api && this.#unsub === undefined) { - // Get block numbers. - const unsub = await api.query.system.number((num: number) => { + if (pApi && this.#sub === undefined) { + const unsub = pApi.query.System.Number.watchValue().subscribe((num) => { // Update class block number. this.blockNumber = num.toString(); @@ -54,23 +39,17 @@ export class BlockNumber implements Unsubscribable { }) ); }); - - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = unsub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } From 3bd6ee383baf2729e894f39efffe15f71285c85e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:23:59 +0700 Subject: [PATCH 10/84] mx rxjs to deps --- packages/app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/package.json b/packages/app/package.json index 34c8de028..7d7a14709 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -63,6 +63,7 @@ "react-i18next": "^15.0.2", "react-router-dom": "^6.23.1", "react-scroll": "^1.9.0", + "rxjs": "^7.8.1", "styled-components": "^6.1.13", "styles": "workspace:*", "ui-buttons": "workspace:*", @@ -73,7 +74,6 @@ "xxhash-wasm": "^1.0.2" }, "devDependencies": { - "rxjs": "^7.8.1", "vite": "^5.2.12", "vite-bundle-visualizer": "^1.2.1", "vite-plugin-checker": "^0.7.0", From 2448c996fc9d5d4ef7c24010b260e766ee538133 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:30:57 +0700 Subject: [PATCH 11/84] network metrics to rxjs --- .../model/Subscribe/NetworkMetrics/index.ts | 74 ++++++++----------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/packages/app/src/model/Subscribe/NetworkMetrics/index.ts b/packages/app/src/model/Subscribe/NetworkMetrics/index.ts index e88874708..66728a7ff 100644 --- a/packages/app/src/model/Subscribe/NetworkMetrics/index.ts +++ b/packages/app/src/model/Subscribe/NetworkMetrics/index.ts @@ -1,61 +1,50 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; -import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; import type { NetworkName } from 'types'; export class NetworkMetrics implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor(network: NetworkName) { this.#network = network; - - // Subscribe immediately. this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); + const { pApi } = ApiController.get(this.#network); - if (api && this.#unsub === undefined) { - const unsub = await api.queryMulti( - [ - api.query.balances.totalIssuance, - api.query.auctions.auctionCounter, - api.query.paraSessionInfo.earliestStoredSession, - api.query.fastUnstake.erasToCheckPerBlock, - api.query.staking.minimumActiveStake, - ], - (result) => { + if (pApi && this.#sub === undefined) { + const sub = combineLatest([ + pApi.query.Balances.TotalIssuance.watchValue(), + pApi.query.Auctions.AuctionCounter.watchValue(), + pApi.query.ParaSessionInfo.EarliestStoredSession.watchValue(), + pApi.query.FastUnstake.ErasToCheckPerBlock.watchValue(), + pApi.query.Staking.MinimumActiveStake.watchValue(), + ]).subscribe( + ([ + totalIssuance, + auctionCounter, + earliestStoredSession, + erasToCheckPerBlock, + minimumActiveStake, + ]) => { const networkMetrics = { - totalIssuance: new BigNumber(result[0].toString()), - auctionCounter: new BigNumber(result[1].toString()), - earliestStoredSession: new BigNumber(result[2].toString()), - fastUnstakeErasToCheckPerBlock: Number( - rmCommas(result[3].toString()) - ), - minimumActiveStake: new BigNumber(result[4].toString()), + totalIssuance: new BigNumber(totalIssuance.toString()), + auctionCounter: new BigNumber(auctionCounter), + earliestStoredSession: new BigNumber(earliestStoredSession), + fastUnstakeErasToCheckPerBlock: Number(erasToCheckPerBlock), + minimumActiveStake: new BigNumber(minimumActiveStake.toString()), }; document.dispatchEvent( @@ -66,22 +55,17 @@ export class NetworkMetrics implements Unsubscribable { } ); - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = sub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } From 99bf80d9849c6be69c418d8013bae443efa77512 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:36:53 +0700 Subject: [PATCH 12/84] `PoolsConfig` to rxjs --- .../src/model/Subscribe/PoolsConfig/index.ts | 116 +++++++++--------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/packages/app/src/model/Subscribe/PoolsConfig/index.ts b/packages/app/src/model/Subscribe/PoolsConfig/index.ts index 1d6911741..4977602e7 100644 --- a/packages/app/src/model/Subscribe/PoolsConfig/index.ts +++ b/packages/app/src/model/Subscribe/PoolsConfig/index.ts @@ -1,28 +1,20 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; -import { rmCommas } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; import type { NetworkName } from 'types'; import { stringToBn } from 'library/Utils'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; export class PoolsConfig implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor(network: NetworkName) { this.#network = network; @@ -31,54 +23,72 @@ export class PoolsConfig implements Unsubscribable { this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); - - if (api && this.#unsub === undefined) { - const unsub = await api.queryMulti( - [ - api.query.nominationPools.counterForPoolMembers, - api.query.nominationPools.counterForBondedPools, - api.query.nominationPools.counterForRewardPools, - api.query.nominationPools.lastPoolId, - api.query.nominationPools.maxPoolMembers, - api.query.nominationPools.maxPoolMembersPerPool, - api.query.nominationPools.maxPools, - api.query.nominationPools.minCreateBond, - api.query.nominationPools.minJoinBond, - api.query.nominationPools.globalMaxCommission, - ], - (result) => { - // format optional configs to BigNumber or null. - const maxPoolMembers = result[4].toHuman() - ? new BigNumber(rmCommas(result[4].toString())) + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const sub = combineLatest([ + pApi.query.NominationPools.CounterForPoolMembers.watchValue(), + pApi.query.NominationPools.CounterForBondedPools.watchValue(), + pApi.query.NominationPools.CounterForRewardPools.watchValue(), + pApi.query.NominationPools.LastPoolId.watchValue(), + pApi.query.NominationPools.MaxPoolMembers.watchValue(), + pApi.query.NominationPools.MaxPoolMembersPerPool.watchValue(), + pApi.query.NominationPools.MaxPools.watchValue(), + pApi.query.NominationPools.MinCreateBond.watchValue(), + pApi.query.NominationPools.MinJoinBond.watchValue(), + pApi.query.NominationPools.GlobalMaxCommission.watchValue(), + ]).subscribe( + ([ + counterForPoolMembers, + counterForBondedPools, + counterForRewardPools, + lastPoolId, + maxPoolMembersRaw, + maxPoolMembersPerPoolRaw, + maxPoolsRaw, + minCreateBond, + minJoinBond, + globalMaxCommission, + ]) => { + // Format globalMaxCommission from a perbill to a percent. + const globalMaxCommissionAsPercent = + BigInt(globalMaxCommission) / 1000000n; + + // Format max pool members to be a BigNumber, or null if it's not set. + const maxPoolMembers = maxPoolMembersRaw + ? new BigNumber(maxPoolMembersRaw.toString()) : null; - const maxPoolMembersPerPool = result[5].toHuman() - ? new BigNumber(rmCommas(result[5].toString())) + // Format max pool members per pool to be a BigNumber, or null if it's not set. + const maxPoolMembersPerPool = maxPoolMembersPerPoolRaw + ? new BigNumber(maxPoolMembersPerPoolRaw.toString()) : null; - const maxPools = result[6].toHuman() - ? new BigNumber(rmCommas(result[6].toString())) + // Format max pools to be a BigNumber, or null if it's not set. + const maxPools = maxPoolsRaw + ? new BigNumber(maxPoolsRaw.toString()) : null; const poolsConfig = { - counterForPoolMembers: stringToBn(result[0].toString()), - counterForBondedPools: stringToBn(result[1].toString()), - counterForRewardPools: stringToBn(result[2].toString()), - lastPoolId: stringToBn(result[3].toString()), + counterForPoolMembers: stringToBn( + counterForPoolMembers.toString() + ), + counterForBondedPools: stringToBn( + counterForBondedPools.toString() + ), + counterForRewardPools: stringToBn( + counterForRewardPools.toString() + ), + lastPoolId: stringToBn(lastPoolId.toString()), maxPoolMembers, maxPoolMembersPerPool, maxPools, - minCreateBond: stringToBn(result[7].toString()), - minJoinBond: stringToBn(result[8].toString()), + minCreateBond: stringToBn(minCreateBond.toString()), + minJoinBond: stringToBn(minJoinBond.toString()), globalMaxCommission: Number( - String(result[9]?.toHuman() || '100%').slice(0, -1) + globalMaxCommissionAsPercent.toString() ), }; @@ -89,23 +99,17 @@ export class PoolsConfig implements Unsubscribable { ); } ); - - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = sub; } } catch (e) { // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } From 9f8ba8064fd1fc1fdac6c6f4cc617fe9b8b05ce8 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 11:42:26 +0700 Subject: [PATCH 13/84] `StakingMetrics` to rxjs --- .../model/Subscribe/StakingMetrics/index.ts | 96 +++++++++---------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/packages/app/src/model/Subscribe/StakingMetrics/index.ts b/packages/app/src/model/Subscribe/StakingMetrics/index.ts index e0032237c..221eac282 100644 --- a/packages/app/src/model/Subscribe/StakingMetrics/index.ts +++ b/packages/app/src/model/Subscribe/StakingMetrics/index.ts @@ -1,19 +1,16 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import type BigNumber from 'bignumber.js'; import type { APIActiveEra } from 'contexts/Api/types'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; import type { NetworkName } from 'types'; import { stringToBn } from 'library/Utils'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; export class StakingMetrics implements Unsubscribable { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // The associated network for this instance. #network: NetworkName; @@ -21,12 +18,8 @@ export class StakingMetrics implements Unsubscribable { #previousEra: BigNumber; - // Unsubscribe object. - #unsub: VoidFn; - - // ------------------------------------------------------ - // Constructor. - // ------------------------------------------------------ + // Active subscription. + #sub: Subscription; constructor( network: NetworkName, @@ -41,42 +34,46 @@ export class StakingMetrics implements Unsubscribable { this.subscribe(); } - // ------------------------------------------------------ - // Subscription. - // ------------------------------------------------------ - subscribe = async (): Promise => { try { - const { api } = ApiController.get(this.#network); + const { pApi } = ApiController.get(this.#network); - if (api && this.#unsub === undefined) { - const unsub = await api.queryMulti( - [ - api.query.staking.counterForValidators, - api.query.staking.maxValidatorsCount, - api.query.staking.validatorCount, - [ - api.query.staking.erasValidatorReward, - this.#previousEra.toString(), - ], - [api.query.staking.erasTotalStake, this.#previousEra.toString()], - api.query.staking.minNominatorBond, - [ - api.query.staking.erasTotalStake, - this.#activeEra.index.toString(), - ], - api.query.staking.counterForNominators, - ], - (result) => { + if (pApi && this.#sub === undefined) { + const sub = combineLatest([ + pApi.query.Staking.CounterForValidators.watchValue(), + pApi.query.Staking.MaxValidatorsCount.watchValue(), + pApi.query.Staking.ValidatorCount.watchValue(), + pApi.query.Staking.ErasValidatorReward.watchValue( + this.#previousEra.toString() + ), + pApi.query.Staking.ErasTotalStake.watchValue( + this.#previousEra.toString() + ), + pApi.query.Staking.MinNominatorBond.watchValue(), + pApi.query.Staking.ErasTotalStake.watchValue( + this.#activeEra.index.toString() + ), + pApi.query.Staking.CounterForNominators.watchValue(), + ]).subscribe( + ([ + counterForValidators, + maxValidatorsCount, + validatorCount, + erasValidatorReward, + lastTotalStake, + minNominatorBond, + totalStaked, + counterForNominators, + ]) => { const stakingMetrics = { - totalValidators: stringToBn(result[0].toString()), - maxValidatorsCount: stringToBn(result[1].toString()), - validatorCount: stringToBn(result[2].toString()), - lastReward: stringToBn(result[3].toString()), - lastTotalStake: stringToBn(result[4].toString()), - minNominatorBond: stringToBn(result[5].toString()), - totalStaked: stringToBn(result[6].toString()), - counterForNominators: stringToBn(result[7].toString()), + totalValidators: stringToBn(counterForValidators.toString()), + maxValidatorsCount: stringToBn(maxValidatorsCount.toString()), + validatorCount: stringToBn(validatorCount.toString()), + lastReward: stringToBn(erasValidatorReward.toString()), + lastTotalStake: stringToBn(lastTotalStake.toString()), + minNominatorBond: stringToBn(minNominatorBond.toString()), + totalStaked: stringToBn(totalStaked.toString()), + counterForNominators: stringToBn(counterForNominators.toString()), }; document.dispatchEvent( @@ -86,22 +83,17 @@ export class StakingMetrics implements Unsubscribable { ); } ); - // Subscription now initialised. Store unsub. - this.#unsub = unsub as unknown as VoidFn; + this.#sub = sub; } } catch (e) { - // Block number subscription failed. + // Subscription failed. } }; - // ------------------------------------------------------ - // Unsubscribe handler. - // ------------------------------------------------------ - // Unsubscribe from class subscription. unsubscribe = (): void => { - if (typeof this.#unsub === 'function') { - this.#unsub(); + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); } }; } From 197ecf0949293c386c1a4e8c4e6365fe5a18d339 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 13:39:28 +0700 Subject: [PATCH 14/84] version info to chainSpecs --- packages/app/src/contexts/Api/defaults.ts | 17 +++---- packages/app/src/contexts/Api/index.tsx | 48 ++++++++----------- packages/app/src/contexts/Api/types.ts | 8 ---- .../app/src/contexts/LedgerHardware/index.tsx | 4 +- packages/app/src/model/Api/index.ts | 17 +++++++ packages/app/src/model/Api/types.ts | 8 ++++ 6 files changed, 55 insertions(+), 47 deletions(-) diff --git a/packages/app/src/contexts/Api/defaults.ts b/packages/app/src/contexts/Api/defaults.ts index 40ebace65..6c03b014a 100644 --- a/packages/app/src/contexts/Api/defaults.ts +++ b/packages/app/src/contexts/Api/defaults.ts @@ -6,7 +6,6 @@ import { stringToU8a } from '@polkadot/util'; import BigNumber from 'bignumber.js'; import type { APIActiveEra, - APIChainState, APIConstants, APIContextInterface, APINetworkMetrics, @@ -15,14 +14,6 @@ import type { PapiChainSpecContext, } from 'contexts/Api/types'; -export const defaultChainState: APIChainState = { - chain: null, - version: { - specVersion: 0, - }, - ss58Prefix: 0, -}; - export const defaultConsts: APIConstants = { bondDuration: new BigNumber(0), maxNominations: new BigNumber(0), @@ -50,6 +41,13 @@ export const defaultChainSpecs: PapiChainSpecContext = { tokenDecimals: 0, tokenSymbol: '', received: false, + authoringVersion: 0, + implName: '', + implVersion: 0, + specName: '', + specVersion: 0, + stateVersion: 0, + transactionVersion: 0, }; export const defaultActiveEra: APIActiveEra = { @@ -84,7 +82,6 @@ export const defaultStakingMetrics: APIStakingMetrics = { export const defaultApiContext: APIContextInterface = { api: null, peopleApi: null, - chainState: defaultChainState, chainSpecs: defaultChainSpecs, isReady: false, apiStatus: 'disconnected', diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 75dbd271f..30622554c 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -7,7 +7,6 @@ import { NetworkList } from 'config/networks'; import type { APIActiveEra, - APIChainState, APIConstants, APIContextInterface, APINetworkMetrics, @@ -21,7 +20,6 @@ import { defaultConsts, defaultActiveEra, defaultApiContext, - defaultChainState, defaultPoolsConfig, defaultNetworkMetrics, defaultStakingMetrics, @@ -106,10 +104,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { setRpcEndpointState(key); }; - // Store chain state. - const [chainState, setChainState] = - useState(defaultChainState); - // Store network constants. const [consts, setConsts] = useState(defaultConsts); @@ -140,23 +134,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // Fetch chain state. Called once `provider` has been initialised. const onApiReady = async () => { - const { api } = ApiController.get(network); - - const newChainState = await Promise.all([ - api.rpc.system.chain(), - api.consts.system.version, - api.consts.system.ss58Prefix, - ]); - - // Check that chain values have been fetched before committing to state. Could be expanded to - // check supported chains. - if (newChainState.every((c) => !!c?.toHuman())) { - const chain = newChainState[0]?.toString(); - const version = newChainState[1]?.toJSON(); - const ss58Prefix = Number(newChainState[2]?.toString()); - setChainState({ chain, version, ss58Prefix }); - } - // Assume chain state is correct and bootstrap network consts. bootstrapNetworkConfig(); }; @@ -225,8 +202,20 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { const handlePapiReady = async (e: Event) => { if (isCustomEvent(e)) { - const { chainType, genesisHash, ss58Format, tokenDecimals, tokenSymbol } = - e.detail; + const { + chainType, + genesisHash, + ss58Format, + tokenDecimals, + tokenSymbol, + authoringVersion, + implName, + implVersion, + specName, + specVersion, + stateVersion, + transactionVersion, + } = e.detail; if (chainType === 'relay') { const newChainSpecs: PapiChainSpecContext = { @@ -234,6 +223,13 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { ss58Format, tokenDecimals, tokenSymbol, + authoringVersion, + implName, + implVersion, + specName, + specVersion, + stateVersion, + transactionVersion, received: true, }; @@ -515,7 +511,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // Reset consts and chain state. setChainSpecs(defaultChainSpecs); setConsts(defaultConsts); - setChainState(defaultChainState); setStateWithRef( defaultNetworkMetrics, setNetworkMetrics, @@ -563,7 +558,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { value={{ api: ApiController.get(network)?.api || null, peopleApi: ApiController.get(`people-${network}`)?.api || null, - chainState, chainSpecs, apiStatus, peopleApiStatus, diff --git a/packages/app/src/contexts/Api/types.ts b/packages/app/src/contexts/Api/types.ts index 5dd28bc86..0ce64eeb7 100644 --- a/packages/app/src/contexts/Api/types.ts +++ b/packages/app/src/contexts/Api/types.ts @@ -6,19 +6,12 @@ import type BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; import type { NetworkName } from '../../types'; import type { ApiStatus, ConnectionType, PapiChainSpec } from 'model/Api/types'; -import type { AnyJson } from '@w3ux/types'; export interface APIProviderProps { children: ReactNode; network: NetworkName; } -export interface APIChainState { - chain: string | null; - version: AnyJson; - ss58Prefix: number; -} - export interface APIConstants { bondDuration: BigNumber; maxNominations: BigNumber; @@ -75,7 +68,6 @@ export interface APIStakingMetrics { export interface APIContextInterface { api: ApiPromise | null; peopleApi: ApiPromise | null; - chainState: APIChainState; chainSpecs: PapiChainSpecContext; isReady: boolean; apiStatus: ApiStatus; diff --git a/packages/app/src/contexts/LedgerHardware/index.tsx b/packages/app/src/contexts/LedgerHardware/index.tsx index ef12b8da8..0b80c4a67 100644 --- a/packages/app/src/contexts/LedgerHardware/index.tsx +++ b/packages/app/src/contexts/LedgerHardware/index.tsx @@ -31,8 +31,8 @@ export const LedgerHardwareProvider = ({ children: ReactNode; }) => { const { t } = useTranslation('modals'); - const { chainState } = useApi(); - const { transactionVersion } = chainState.version; + const { chainSpecs } = useApi(); + const { transactionVersion } = chainSpecs; // Store whether a Ledger device task is in progress. const [isExecuting, setIsExecutingState] = useState(false); diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index 8fcf8f825..f8c15e9d4 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -175,15 +175,32 @@ export class Api { async fetchChainSpec() { try { const chainSpecData = await this.#papiClient.getChainSpecData(); + const version = await this.#pApi.constants.System.Version(); const { genesisHash, properties } = chainSpecData; const { ss58Format, tokenDecimals, tokenSymbol } = properties; + const { + authoring_version: authoringVersion, + impl_name: implName, + impl_version: implVersion, + spec_name: specName, + spec_version: specVersion, + state_version: stateVersion, + transaction_version: transactionVersion, + } = version; this.#papiChainSpec = { genesisHash, ss58Format, tokenDecimals, tokenSymbol, + authoringVersion, + implName, + implVersion, + specName, + specVersion, + stateVersion, + transactionVersion, }; // Dispatch 'papi-ready' event to let contexts populate constants. diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index eaae0bf6f..efbea3b02 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -23,7 +23,15 @@ export interface PapiChainSpec { ss58Format: number; tokenDecimals: number; tokenSymbol: string; + authoringVersion: number; + implName: string; + implVersion: number; + specName: string; + specVersion: number; + stateVersion: number; + transactionVersion: number; } + export type PapiReadyEvent = PapiChainSpec & { network: NetworkName | SystemChainId; chainType: string; From a4eeb55487b90dad3de61326f7927c7580d8f85b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 13:45:20 +0700 Subject: [PATCH 15/84] rename --- packages/app/src/model/Query/Era/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app/src/model/Query/Era/index.ts b/packages/app/src/model/Query/Era/index.ts index fb83230c7..dd1bf8f9a 100644 --- a/packages/app/src/model/Query/Era/index.ts +++ b/packages/app/src/model/Query/Era/index.ts @@ -6,10 +6,10 @@ import type { PapiApi } from 'model/Api/types'; export class Era { // Fetch network constants. - async fetch(api: PapiApi) { + async fetch(pApi: PapiApi) { let result; try { - const { index, start } = await api.query.Staking.ActiveEra.getValue(); + const { index, start } = await pApi.query.Staking.ActiveEra.getValue(); result = { start, index, From 286d2f562f9df03190c0b9d81ec164b3fed450ce Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 16:24:19 +0700 Subject: [PATCH 16/84] `ValidatorEntries` to query class --- .../Validators/ValidatorEntries/index.tsx | 45 +++++++++++-------- packages/app/src/library/Utils/index.ts | 4 ++ .../src/model/Query/ValidatorEntries/index.ts | 16 +++++++ 3 files changed, 46 insertions(+), 19 deletions(-) create mode 100644 packages/app/src/model/Query/ValidatorEntries/index.ts diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index 452b4be00..3b510d52e 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -33,6 +33,9 @@ import { getLocalEraValidators, setLocalEraValidators } from '../Utils'; import { useErasPerDay } from 'hooks/useErasPerDay'; import { IdentitiesController } from 'controllers/Identities'; import type { AnyJson, Sync } from '@w3ux/types'; +import { ValidatorEntries } from 'model/Query/ValidatorEntries'; +import { ApiController } from 'controllers/Api'; +import { perbillToPercent } from 'library/Utils'; export const ValidatorsContext = createContext( defaultValidatorsContext @@ -240,34 +243,38 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetch validator entries and format the returning data. const getValidatorEntries = async () => { - if (!isReady || !api) { + if (!isReady) { return defaultValidatorsData; } - const result = await api.query.staking.validators.entries(); + const { pApi } = ApiController.get(network); + const result = await new ValidatorEntries(pApi).fetch(); const entries: Validator[] = []; let notFullCommissionCount = 0; let totalNonAllCommission = new BigNumber(0); - result.forEach(([a, p]: AnyApi) => { - const address = a.toHuman().pop(); - const prefs = p.toHuman(); - const commission = new BigNumber(prefs.commission.replace(/%/g, '')); + result.forEach( + ({ keyArgs: [address], value: { commission, blocked } }: AnyApi) => { + const commissionAsPercent = new BigNumber( + perbillToPercent(new BigNumber(commission)).toString() + ); + + if (!commissionAsPercent.isEqualTo(100)) { + totalNonAllCommission = + totalNonAllCommission.plus(commissionAsPercent); + } else { + notFullCommissionCount++; + } - if (!commission.isEqualTo(100)) { - totalNonAllCommission = totalNonAllCommission.plus(commission); - } else { - notFullCommissionCount++; + entries.push({ + address, + prefs: { + commission: Number(commissionAsPercent.toFixed(2)), + blocked, + }, + }); } - - entries.push({ - address, - prefs: { - commission: Number(commission.toFixed(2)), - blocked: prefs.blocked, - }, - }); - }); + ); return { entries, notFullCommissionCount, totalNonAllCommission }; }; diff --git a/packages/app/src/library/Utils/index.ts b/packages/app/src/library/Utils/index.ts index e0854c009..8aa038460 100644 --- a/packages/app/src/library/Utils/index.ts +++ b/packages/app/src/library/Utils/index.ts @@ -78,3 +78,7 @@ export const timeleftAsString = ( } return str; }; + +// Convert a perbill BigNumber value into a percentage. +export const perbillToPercent = (value: BigNumber): BigNumber => + value.dividedBy('10000000'); diff --git a/packages/app/src/model/Query/ValidatorEntries/index.ts b/packages/app/src/model/Query/ValidatorEntries/index.ts new file mode 100644 index 000000000..2c248b348 --- /dev/null +++ b/packages/app/src/model/Query/ValidatorEntries/index.ts @@ -0,0 +1,16 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ValidatorEntries { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch() { + return await this.#pApi.query.Staking.Validators.getEntries(); + } +} From 6dedac21e6b5cc45e07437f786e5d49417d5af40 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 16:27:30 +0700 Subject: [PATCH 17/84] fix, api to constructor --- packages/app/src/contexts/Api/index.tsx | 17 ++--- packages/app/src/model/Query/Era/index.ts | 12 +++- .../app/src/model/Query/NetworkMeta/index.ts | 65 +++++++++++-------- 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 30622554c..279eac3cd 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -132,12 +132,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { const [chainSpecs, setChainSpecs] = useState(defaultChainSpecs); - // Fetch chain state. Called once `provider` has been initialised. - const onApiReady = async () => { - // Assume chain state is correct and bootstrap network consts. - bootstrapNetworkConfig(); - }; - // Bootstrap app-wide chain state. const bootstrapNetworkConfig = async () => { const apiInstance = ApiController.get(network); @@ -146,16 +140,16 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { // 1. Fetch network data for bootstrapping app state: // Get active and previous era. - const { activeEra: newActiveEra, previousEra } = await new Era().fetch( + const { activeEra: newActiveEra, previousEra } = await new Era( pApi - ); + ).fetch(); // Get network meta data related to staking and pools. const { networkMetrics: newNetworkMetrics, poolsConfig: newPoolsConfig, stakingMetrics: newStakingMetrics, - } = await new NetworkMeta().fetch(pApi, newActiveEra, previousEra); + } = await new NetworkMeta(pApi).fetch(newActiveEra, previousEra); // 2. Populate all config state: @@ -291,6 +285,8 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { fastUnstakeDeposit: new BigNumber(fastUnstakeDeposit.toString()), poolsPalletId, }); + + bootstrapNetworkConfig(); } } }; @@ -326,9 +322,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { return; } switch (status) { - case 'ready': - onApiReady(); - break; case 'connecting': setApiStatus('connecting'); break; diff --git a/packages/app/src/model/Query/Era/index.ts b/packages/app/src/model/Query/Era/index.ts index dd1bf8f9a..284b78929 100644 --- a/packages/app/src/model/Query/Era/index.ts +++ b/packages/app/src/model/Query/Era/index.ts @@ -5,11 +5,19 @@ import BigNumber from 'bignumber.js'; import type { PapiApi } from 'model/Api/types'; export class Era { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + // Fetch network constants. - async fetch(pApi: PapiApi) { + async fetch() { let result; try { - const { index, start } = await pApi.query.Staking.ActiveEra.getValue(); + const { index, start } = + await this.#pApi.query.Staking.ActiveEra.getValue(); + result = { start, index, diff --git a/packages/app/src/model/Query/NetworkMeta/index.ts b/packages/app/src/model/Query/NetworkMeta/index.ts index b4e14211b..917d3334d 100644 --- a/packages/app/src/model/Query/NetworkMeta/index.ts +++ b/packages/app/src/model/Query/NetworkMeta/index.ts @@ -3,13 +3,20 @@ import BigNumber from 'bignumber.js'; import type { APIActiveEra } from 'contexts/Api/types'; -import { stringToBn } from 'library/Utils'; +import { perbillToPercent, stringToBn } from 'library/Utils'; import type { PapiApi } from 'model/Api/types'; export class NetworkMeta { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + // Fetch network constants. - async fetch(pApi: PapiApi, activeEra: APIActiveEra, previousEra: BigNumber) { - const totalIssuance = await pApi.query.Balances.TotalIssuance.getValue(); + async fetch(activeEra: APIActiveEra, previousEra: BigNumber) { + const totalIssuance = + await this.#pApi.query.Balances.TotalIssuance.getValue(); const [ auctionCounter, @@ -35,32 +42,38 @@ export class NetworkMeta { minNominatorBond, activeEraErasTotalStake, ] = await Promise.all([ - pApi.query.Auctions.AuctionCounter.getValue(), - pApi.query.ParaSessionInfo.EarliestStoredSession.getValue(), - pApi.query.FastUnstake.ErasToCheckPerBlock.getValue(), - pApi.query.Staking.MinimumActiveStake.getValue(), - pApi.query.NominationPools.CounterForPoolMembers.getValue(), - pApi.query.NominationPools.CounterForBondedPools.getValue(), - pApi.query.NominationPools.CounterForRewardPools.getValue(), - pApi.query.NominationPools.LastPoolId.getValue(), - pApi.query.NominationPools.MaxPoolMembers.getValue(), - pApi.query.NominationPools.MaxPoolMembersPerPool.getValue(), - pApi.query.NominationPools.MaxPools.getValue(), - pApi.query.NominationPools.MinCreateBond.getValue(), - pApi.query.NominationPools.MinJoinBond.getValue(), - pApi.query.NominationPools.GlobalMaxCommission.getValue(), - pApi.query.Staking.CounterForNominators.getValue(), - pApi.query.Staking.CounterForValidators.getValue(), - pApi.query.Staking.MaxValidatorsCount.getValue(), - pApi.query.Staking.ValidatorCount.getValue(), - pApi.query.Staking.ErasValidatorReward.getValue(previousEra.toString()), - pApi.query.Staking.ErasTotalStake.getValue(previousEra.toString()), - pApi.query.Staking.MinNominatorBond.getValue(), - pApi.query.Staking.ErasTotalStake.getValue(activeEra.index.toString()), + this.#pApi.query.Auctions.AuctionCounter.getValue(), + this.#pApi.query.ParaSessionInfo.EarliestStoredSession.getValue(), + this.#pApi.query.FastUnstake.ErasToCheckPerBlock.getValue(), + this.#pApi.query.Staking.MinimumActiveStake.getValue(), + this.#pApi.query.NominationPools.CounterForPoolMembers.getValue(), + this.#pApi.query.NominationPools.CounterForBondedPools.getValue(), + this.#pApi.query.NominationPools.CounterForRewardPools.getValue(), + this.#pApi.query.NominationPools.LastPoolId.getValue(), + this.#pApi.query.NominationPools.MaxPoolMembers.getValue(), + this.#pApi.query.NominationPools.MaxPoolMembersPerPool.getValue(), + this.#pApi.query.NominationPools.MaxPools.getValue(), + this.#pApi.query.NominationPools.MinCreateBond.getValue(), + this.#pApi.query.NominationPools.MinJoinBond.getValue(), + this.#pApi.query.NominationPools.GlobalMaxCommission.getValue(), + this.#pApi.query.Staking.CounterForNominators.getValue(), + this.#pApi.query.Staking.CounterForValidators.getValue(), + this.#pApi.query.Staking.MaxValidatorsCount.getValue(), + this.#pApi.query.Staking.ValidatorCount.getValue(), + this.#pApi.query.Staking.ErasValidatorReward.getValue( + previousEra.toString() + ), + this.#pApi.query.Staking.ErasTotalStake.getValue(previousEra.toString()), + this.#pApi.query.Staking.MinNominatorBond.getValue(), + this.#pApi.query.Staking.ErasTotalStake.getValue( + activeEra.index.toString() + ), ]); // Format globalMaxCommission from a perbill to a percent. - const globalMaxCommissionAsPercent = BigInt(globalMaxCommission) / 1000000n; + const globalMaxCommissionAsPercent = perbillToPercent( + new BigNumber(globalMaxCommission) + ); // Format max pool members to be a BigNumber, or null if it's not set. const maxPoolMembers = maxPoolMembersRaw From 3617108edf6a8c6606601b58168b9530fa8703d3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 16:35:33 +0700 Subject: [PATCH 18/84] `ErasStakersOverview` to Query class --- packages/app/src/contexts/Staking/index.tsx | 24 ++++++++++--------- .../model/Query/ErasStakersOverview/index.tsx | 16 +++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 packages/app/src/model/Query/ErasStakersOverview/index.tsx diff --git a/packages/app/src/contexts/Staking/index.tsx b/packages/app/src/contexts/Staking/index.tsx index bc8ea15db..50a9bf25d 100644 --- a/packages/app/src/contexts/Staking/index.tsx +++ b/packages/app/src/contexts/Staking/index.tsx @@ -25,6 +25,9 @@ import { defaultEraStakers, defaultStakingContext } from './defaults'; import { setLocalEraExposures, getLocalEraExposures } from './Utils'; import type { NominationStatus } from 'library/ValidatorList/ValidatorItem/types'; import { SyncController } from 'controllers/Sync'; +import { ErasStakersOverview } from 'model/Query/ErasStakersOverview'; +import { ApiController } from 'controllers/Api'; +import type { AnyJson } from '@w3ux/types'; const worker = new Worker(); @@ -38,9 +41,9 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { const { getBondedAccount } = useBonded(); const { networkData, network } = useNetwork(); const { getLedger, getNominations } = useBalances(); + const { isReady, api, activeEra, apiStatus } = useApi(); const { accounts: connectAccounts } = useImportedAccounts(); const { activeAccount, getActiveAccount } = useActiveAccounts(); - const { isReady, api, apiStatus, activeEra } = useApi(); // Store eras stakers in state. const [eraStakers, setEraStakers] = useState(defaultEraStakers); @@ -225,15 +228,14 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { return []; } - const overview: AnyApi = - await api.query.staking.erasStakersOverview.entries(era); + const { pApi } = ApiController.get(network); + const overviewNew = await new ErasStakersOverview(pApi).fetch(era); - const validators = overview.reduce( - (prev: Record, [keys, value]: AnyApi) => { - const validator = keys.toHuman()[1]; - const { own, total } = value.toHuman(); - return { ...prev, [validator]: { own, total } }; - }, + const validators: Record = overviewNew.reduce( + ( + prev: Record, + { keyArgs: [, validator], value: { own, total } }: AnyApi + ) => ({ ...prev, [validator]: { own, total } }), {} ); const validatorKeys = Object.keys(validators); @@ -263,8 +265,8 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { result.push({ keys: [rmCommas(era), validator], val: { - total: rmCommas(total), - own: rmCommas(own), + total: total.toString(), + own: own.toString(), others: others.map(({ who, value }) => ({ who, value: rmCommas(value), diff --git a/packages/app/src/model/Query/ErasStakersOverview/index.tsx b/packages/app/src/model/Query/ErasStakersOverview/index.tsx new file mode 100644 index 000000000..8c42b5d3a --- /dev/null +++ b/packages/app/src/model/Query/ErasStakersOverview/index.tsx @@ -0,0 +1,16 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasStakersOverview { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch(era: string) { + return await this.#pApi.query.Staking.ErasStakersOverview.getEntries(era); + } +} From 659776b8df92657306447104dcc259d2e8d6b8ca Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 15 Nov 2024 16:44:52 +0700 Subject: [PATCH 19/84] `ErasStakersPaged` to Query class --- packages/app/src/contexts/Staking/index.tsx | 42 ++++++++----------- .../app/src/model/ErasStakersPaged/index.tsx | 19 +++++++++ 2 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 packages/app/src/model/ErasStakersPaged/index.tsx diff --git a/packages/app/src/contexts/Staking/index.tsx b/packages/app/src/contexts/Staking/index.tsx index 50a9bf25d..2ca9f5b1e 100644 --- a/packages/app/src/contexts/Staking/index.tsx +++ b/packages/app/src/contexts/Staking/index.tsx @@ -1,7 +1,7 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { rmCommas, setStateWithRef } from '@w3ux/utils'; +import { setStateWithRef } from '@w3ux/utils'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; import { useBalances } from 'contexts/Balances'; @@ -9,7 +9,6 @@ import type { ExternalAccount } from '@w3ux/react-connect-kit/types'; import type { EraStakers, Exposure, - ExposureOther, StakingContextInterface, } from 'contexts/Staking/types'; import type { AnyApi, MaybeAddress } from 'types'; @@ -28,6 +27,7 @@ import { SyncController } from 'controllers/Sync'; import { ErasStakersOverview } from 'model/Query/ErasStakersOverview'; import { ApiController } from 'controllers/Api'; import type { AnyJson } from '@w3ux/types'; +import { ErasStakersPaged } from 'model/ErasStakersPaged'; const worker = new Worker(); @@ -229,9 +229,8 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { } const { pApi } = ApiController.get(network); - const overviewNew = await new ErasStakersOverview(pApi).fetch(era); - - const validators: Record = overviewNew.reduce( + const overview = await new ErasStakersOverview(pApi).fetch(era); + const validators: Record = overview.reduce( ( prev: Record, { keyArgs: [, validator], value: { own, total } }: AnyApi @@ -241,36 +240,31 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { const validatorKeys = Object.keys(validators); const pagedResults = await Promise.all( - validatorKeys.map((v) => - api.query.staking.erasStakersPaged.entries(era, v) - ) + validatorKeys.map((v) => new ErasStakersPaged(pApi).fetch(era, v)) ); const result: Exposure[] = []; let i = 0; - for (const pagedResult of pagedResults) { + for (const [ + { + keyArgs, + value: { others }, + }, + ] of pagedResults) { const validator = validatorKeys[i]; const { own, total } = validators[validator]; - const others = pagedResult.reduce( - (prev: ExposureOther[], [, v]: AnyApi) => { - const o = v.toHuman()?.others || []; - if (!o.length) { - return prev; - } - return prev.concat(o); - }, - [] - ); result.push({ - keys: [rmCommas(era), validator], + keys: [keyArgs[0].toString(), validator], val: { total: total.toString(), own: own.toString(), - others: others.map(({ who, value }) => ({ - who, - value: rmCommas(value), - })), + others: others.map( + ({ who, value }: { who: string; value: bigint }) => ({ + who, + value: value.toString(), + }) + ), }, }); i++; diff --git a/packages/app/src/model/ErasStakersPaged/index.tsx b/packages/app/src/model/ErasStakersPaged/index.tsx new file mode 100644 index 000000000..a7a3a57f3 --- /dev/null +++ b/packages/app/src/model/ErasStakersPaged/index.tsx @@ -0,0 +1,19 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasStakersPaged { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch(era: string, validator: string) { + return await this.#pApi.query.Staking.ErasStakersPaged.getEntries( + era, + validator + ); + } +} From da70698a435632463cef744715cf32404edc1c89 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 16 Nov 2024 09:39:25 +0700 Subject: [PATCH 20/84] add `BondedPoolsEntries` Query class --- .../src/contexts/Pools/BondedPools/index.tsx | 25 ++++--- .../Validators/ValidatorEntries/index.tsx | 4 +- packages/app/src/library/Utils/index.ts | 10 ++- .../model/Query/BondedPoolsEntries/index.tsx | 66 +++++++++++++++++++ .../app/src/model/Query/NetworkMeta/index.ts | 6 +- 5 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 packages/app/src/model/Query/BondedPoolsEntries/index.tsx diff --git a/packages/app/src/contexts/Pools/BondedPools/index.tsx b/packages/app/src/contexts/Pools/BondedPools/index.tsx index 02a502981..e089231cf 100644 --- a/packages/app/src/contexts/Pools/BondedPools/index.tsx +++ b/packages/app/src/contexts/Pools/BondedPools/index.tsx @@ -23,6 +23,8 @@ import { useApi } from '../../Api'; import { defaultBondedPoolsContext } from './defaults'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; import { SyncController } from 'controllers/Sync'; +import { BondedPoolsEntries } from 'model/Query/BondedPoolsEntries'; +import { ApiController } from 'controllers/Api'; export const BondedPoolsContext = createContext( defaultBondedPoolsContext @@ -63,21 +65,26 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { // Fetch all bonded pool entries and their metadata. const fetchBondedPools = async () => { - if (!api || bondedPoolsSynced.current !== 'unsynced') { + const { pApi } = ApiController.get(network); + + if (!api || !pApi || bondedPoolsSynced.current !== 'unsynced') { return; } bondedPoolsSynced.current = 'syncing'; const ids: number[] = []; - // Fetch bonded pools entries. - const bondedPoolsMulti = - await api.query.nominationPools.bondedPools.entries(); - let exposures = bondedPoolsMulti.map(([keys, val]: AnyApi) => { - const id = keys.toHuman()[0]; - ids.push(id); - return getPoolWithAddresses(id, val.toHuman()); - }); + // Get and format bonded pool entries. + const bondedPoolsEntries = ( + await new BondedPoolsEntries(pApi).fetch() + ).format(); + + let exposures = Object.entries(bondedPoolsEntries).map( + ([id, pool]: AnyApi) => { + ids.push(id); + return getPoolWithAddresses(id, pool); + } + ); exposures = shuffle(exposures); setStateWithRef(exposures, setBondedPools, bondedPoolsRef); diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index 3b510d52e..56b6ce614 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -255,9 +255,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { let totalNonAllCommission = new BigNumber(0); result.forEach( ({ keyArgs: [address], value: { commission, blocked } }: AnyApi) => { - const commissionAsPercent = new BigNumber( - perbillToPercent(new BigNumber(commission)).toString() - ); + const commissionAsPercent = perbillToPercent(commission); if (!commissionAsPercent.isEqualTo(100)) { totalNonAllCommission = diff --git a/packages/app/src/library/Utils/index.ts b/packages/app/src/library/Utils/index.ts index 8aa038460..40ef9a3f7 100644 --- a/packages/app/src/library/Utils/index.ts +++ b/packages/app/src/library/Utils/index.ts @@ -80,5 +80,11 @@ export const timeleftAsString = ( }; // Convert a perbill BigNumber value into a percentage. -export const perbillToPercent = (value: BigNumber): BigNumber => - value.dividedBy('10000000'); +export const perbillToPercent = ( + value: BigNumber | bigint | number +): BigNumber => { + if (typeof value === 'bigint' || typeof value === 'number') { + value = new BigNumber(value.toString()); + } + return value.dividedBy('10000000'); +}; diff --git a/packages/app/src/model/Query/BondedPoolsEntries/index.tsx b/packages/app/src/model/Query/BondedPoolsEntries/index.tsx new file mode 100644 index 000000000..00bc867e8 --- /dev/null +++ b/packages/app/src/model/Query/BondedPoolsEntries/index.tsx @@ -0,0 +1,66 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import BigNumber from 'bignumber.js'; +import { perbillToPercent } from 'library/Utils'; +import type { PapiApi } from 'model/Api/types'; +import type { AnyApi } from 'types'; + +export class BondedPoolsEntries { + #pApi: PapiApi; + + bondedPools: AnyApi = {}; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + async fetch() { + this.bondedPools = + await this.#pApi.query.NominationPools.BondedPools.getEntries(); + return this; + } + + format() { + return Object.fromEntries( + this.bondedPools.map( + ({ keyArgs, value }: { keyArgs: [number]; value: AnyApi }) => { + const id = keyArgs[0]; + + const maybeCommissionCurrent = value.commission.current; + const commissionCurrent = !maybeCommissionCurrent + ? null + : [ + perbillToPercent(maybeCommissionCurrent[0]).toString(), + maybeCommissionCurrent[1], + ]; + + const commissionMax = value.commission.max; + const commissionMaxPercent = !commissionMax + ? null + : perbillToPercent(new BigNumber(value.commission.max)); + + const commissionChangeRate = value.commission.change_rate; + + const commission = { + current: commissionCurrent, + claimPermission: value.commission.claim_permission?.type || null, + max: commissionMaxPercent, + changeRate: commissionChangeRate || null, + throttleFrom: value.commission.throttle_from || null, + }; + + const pool = { + commission, + points: value.points.toString(), + memberCounter: value.member_counter.toString(), + roles: value.roles, + state: value.state.type, + }; + + return [id, pool]; + } + ) + ); + } +} diff --git a/packages/app/src/model/Query/NetworkMeta/index.ts b/packages/app/src/model/Query/NetworkMeta/index.ts index 917d3334d..f8e99dc2b 100644 --- a/packages/app/src/model/Query/NetworkMeta/index.ts +++ b/packages/app/src/model/Query/NetworkMeta/index.ts @@ -71,9 +71,9 @@ export class NetworkMeta { ]); // Format globalMaxCommission from a perbill to a percent. - const globalMaxCommissionAsPercent = perbillToPercent( - new BigNumber(globalMaxCommission) - ); + const globalMaxCommissionAsPercent = !globalMaxCommission + ? new BigNumber(0) + : perbillToPercent(globalMaxCommission); // Format max pool members to be a BigNumber, or null if it's not set. const maxPoolMembers = maxPoolMembersRaw From b80900199c249166faa91bbd12b2284a98a90ecb Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 16 Nov 2024 09:42:02 +0700 Subject: [PATCH 21/84] entries to own directory --- packages/app/src/contexts/Pools/BondedPools/index.tsx | 6 ++---- .../app/src/contexts/Validators/ValidatorEntries/index.tsx | 4 ++-- .../BondedPoolsEntries => Entries/BondedPools}/index.tsx | 2 +- .../{Query/ValidatorEntries => Entries/Validators}/index.ts | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) rename packages/app/src/model/{Query/BondedPoolsEntries => Entries/BondedPools}/index.tsx (98%) rename packages/app/src/model/{Query/ValidatorEntries => Entries/Validators}/index.ts (91%) diff --git a/packages/app/src/contexts/Pools/BondedPools/index.tsx b/packages/app/src/contexts/Pools/BondedPools/index.tsx index e089231cf..89ab2984d 100644 --- a/packages/app/src/contexts/Pools/BondedPools/index.tsx +++ b/packages/app/src/contexts/Pools/BondedPools/index.tsx @@ -23,7 +23,7 @@ import { useApi } from '../../Api'; import { defaultBondedPoolsContext } from './defaults'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; import { SyncController } from 'controllers/Sync'; -import { BondedPoolsEntries } from 'model/Query/BondedPoolsEntries'; +import { BondedPools } from 'model/Entries/BondedPools'; import { ApiController } from 'controllers/Api'; export const BondedPoolsContext = createContext( @@ -75,9 +75,7 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { const ids: number[] = []; // Get and format bonded pool entries. - const bondedPoolsEntries = ( - await new BondedPoolsEntries(pApi).fetch() - ).format(); + const bondedPoolsEntries = (await new BondedPools(pApi).fetch()).format(); let exposures = Object.entries(bondedPoolsEntries).map( ([id, pool]: AnyApi) => { diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index 56b6ce614..ac54c395c 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -33,7 +33,7 @@ import { getLocalEraValidators, setLocalEraValidators } from '../Utils'; import { useErasPerDay } from 'hooks/useErasPerDay'; import { IdentitiesController } from 'controllers/Identities'; import type { AnyJson, Sync } from '@w3ux/types'; -import { ValidatorEntries } from 'model/Query/ValidatorEntries'; +import { Validators } from 'model/Entries/Validators'; import { ApiController } from 'controllers/Api'; import { perbillToPercent } from 'library/Utils'; @@ -248,7 +248,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } const { pApi } = ApiController.get(network); - const result = await new ValidatorEntries(pApi).fetch(); + const result = await new Validators(pApi).fetch(); const entries: Validator[] = []; let notFullCommissionCount = 0; diff --git a/packages/app/src/model/Query/BondedPoolsEntries/index.tsx b/packages/app/src/model/Entries/BondedPools/index.tsx similarity index 98% rename from packages/app/src/model/Query/BondedPoolsEntries/index.tsx rename to packages/app/src/model/Entries/BondedPools/index.tsx index 00bc867e8..a6ae9466b 100644 --- a/packages/app/src/model/Query/BondedPoolsEntries/index.tsx +++ b/packages/app/src/model/Entries/BondedPools/index.tsx @@ -6,7 +6,7 @@ import { perbillToPercent } from 'library/Utils'; import type { PapiApi } from 'model/Api/types'; import type { AnyApi } from 'types'; -export class BondedPoolsEntries { +export class BondedPools { #pApi: PapiApi; bondedPools: AnyApi = {}; diff --git a/packages/app/src/model/Query/ValidatorEntries/index.ts b/packages/app/src/model/Entries/Validators/index.ts similarity index 91% rename from packages/app/src/model/Query/ValidatorEntries/index.ts rename to packages/app/src/model/Entries/Validators/index.ts index 2c248b348..1f64955f5 100644 --- a/packages/app/src/model/Query/ValidatorEntries/index.ts +++ b/packages/app/src/model/Entries/Validators/index.ts @@ -3,7 +3,7 @@ import type { PapiApi } from 'model/Api/types'; -export class ValidatorEntries { +export class Validators { #pApi: PapiApi; constructor(pApi: PapiApi) { From 40fd03eccaa03635042cd9717afe8d4d819464af Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 16 Nov 2024 10:10:52 +0700 Subject: [PATCH 22/84] paged result fix --- packages/app/src/contexts/Staking/index.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/app/src/contexts/Staking/index.tsx b/packages/app/src/contexts/Staking/index.tsx index 2ca9f5b1e..e1ff18150 100644 --- a/packages/app/src/contexts/Staking/index.tsx +++ b/packages/app/src/contexts/Staking/index.tsx @@ -245,12 +245,15 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { const result: Exposure[] = []; let i = 0; - for (const [ - { + // NOTE: Only one page is fetched for each validator for now. + for (const pages of pagedResults) { + const page = pages[0]; + + const { keyArgs, value: { others }, - }, - ] of pagedResults) { + } = page; + const validator = validatorKeys[i]; const { own, total } = validators[validator]; From c16369a81547b8c7fdfeb3a9ede5d97f25e5e341 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 16 Nov 2024 10:11:35 +0700 Subject: [PATCH 23/84] `Bonded` to subscription --- packages/app/src/contexts/Bonded/index.tsx | 109 ++++++++---------- packages/app/src/contexts/Staking/index.tsx | 2 +- .../src/controllers/Subscriptions/types.ts | 2 + .../app/src/model/Subscribe/Bonded/index.tsx | 64 ++++++++++ packages/app/src/types.ts | 2 + 5 files changed, 114 insertions(+), 65 deletions(-) create mode 100644 packages/app/src/model/Subscribe/Bonded/index.tsx diff --git a/packages/app/src/contexts/Bonded/index.tsx b/packages/app/src/contexts/Bonded/index.tsx index 8e1a9286c..ac7333ae6 100644 --- a/packages/app/src/contexts/Bonded/index.tsx +++ b/packages/app/src/contexts/Bonded/index.tsx @@ -1,7 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import { addedTo, matchedProperties, @@ -9,9 +8,9 @@ import { setStateWithRef, } from '@w3ux/utils'; import type { ReactNode } from 'react'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; +import { createContext, useContext, useRef, useState } from 'react'; import { useApi } from 'contexts/Api'; -import type { AnyApi, MaybeAddress } from 'types'; +import type { MaybeAddress } from 'types'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { useNetwork } from 'contexts/Network'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; @@ -19,6 +18,10 @@ import { useOtherAccounts } from 'contexts/Connect/OtherAccounts'; import { useExternalAccounts } from 'contexts/Connect/ExternalAccounts'; import * as defaults from './defaults'; import type { BondedAccount, BondedContextInterface } from './types'; +import { useEventListener } from 'usehooks-ts'; +import { isCustomEvent } from 'controllers/utils'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { Bonded } from 'model/Subscribe/Bonded'; export const BondedContext = createContext( defaults.defaultBondedContext @@ -27,8 +30,8 @@ export const BondedContext = createContext( export const useBonded = () => useContext(BondedContext); export const BondedProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); - const { api, isReady } = useApi(); const { accounts } = useImportedAccounts(); const { addExternalAccount } = useExternalAccounts(); const { addOrReplaceOtherAccount } = useOtherAccounts(); @@ -37,8 +40,6 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { const [bondedAccounts, setBondedAccounts] = useState([]); const bondedAccountsRef = useRef(bondedAccounts); - const unsubs = useRef>({}); - // Handle the syncing of accounts on accounts change. const handleSyncAccounts = () => { // Sync removed accounts. @@ -48,23 +49,21 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { ]).map(({ address }) => address); removed?.forEach((address) => { - const unsub = unsubs.current[address]; - if (unsub) { - unsub(); - } + SubscriptionsController.remove(network, `bonded-${address}`); }); - - unsubs.current = Object.fromEntries( - Object.entries(unsubs.current).filter(([key]) => !removed.includes(key)) - ); }; // Sync added accounts. const handleAddedAccounts = () => { const added = addedTo(accounts, bondedAccountsRef.current, ['address']); if (added.length) { - // Subscribe to all newly added accounts bonded and nominator status. - added.map(({ address }) => subscribeToBondedAccount(address)); + added.forEach(({ address }) => + SubscriptionsController.set( + network, + `bonded-${address}`, + new Bonded(network, address) + ) + ); } }; @@ -76,58 +75,41 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { bondedAccountsRef ); }; + handleRemovedAccounts(); handleAddedAccounts(); handleExistingAccounts(); }; - // Subscribe to account, get controller and nominations. - const subscribeToBondedAccount = async (address: string) => { - if (!api) { - return undefined; - } + const getBondedAccount = (address: MaybeAddress) => + bondedAccountsRef.current.find((a) => a.address === address)?.bonded || + null; - const unsub = await api.queryMulti( - [[api.query.staking.bonded, address]], - async ([controller]) => { - const newAccount: BondedAccount = { - address, - }; - - // set account bonded (controller) or null - let newController = controller.unwrapOr(null); - newController = - newController === null - ? null - : (newController.toHuman() as string | null); - newAccount.bonded = newController; - - // add bonded (controller) account as external account if not presently imported - if (newController) { - if (accounts.find((s) => s.address === newController) === undefined) { - const result = addExternalAccount(newController, 'system'); - if (result) { - addOrReplaceOtherAccount(result.account, result.type); - } + // Handle `polkadot-api` events. + const handleNewBondedAccount = (e: Event) => { + if (isCustomEvent(e)) { + const { account } = e.detail; + const { bonded, address } = account; + + // Add bonded (controller) account as external account if not presently imported + if (bonded) { + if (accounts.find((s) => s.address === bonded) === undefined) { + const result = addExternalAccount(bonded, 'system'); + if (result) { + addOrReplaceOtherAccount(result.account, result.type); } } - - // remove stale account if it's already in list. - const newBonded = Object.values(bondedAccountsRef.current) - .filter((a) => a.address !== address) - .concat(newAccount); - - setStateWithRef(newBonded, setBondedAccounts, bondedAccountsRef); } - ); - unsubs.current[address] = unsub; - return unsub; - }; + // Remove stale account if it's already in list. + const newBonded = Object.values(bondedAccountsRef.current) + .filter((a) => a.address !== address) + .concat(account); - const getBondedAccount = (address: MaybeAddress) => - bondedAccountsRef.current.find((a) => a.address === address)?.bonded || - null; + // Update bonded accounts state. + setStateWithRef(newBonded, setBondedAccounts, bondedAccountsRef); + } + }; // Handle accounts sync on connected accounts change. useEffectIgnoreInitial(() => { @@ -136,14 +118,13 @@ export const BondedProvider = ({ children }: { children: ReactNode }) => { } }, [accounts, network, isReady]); - // Unsubscribe from subscriptions on unmount. - useEffect( - () => () => - Object.values(unsubs.current).forEach((unsub) => { - unsub(); - }), - [] + // Handle new bonded account events. + useEventListener( + 'new-bonded-account', + handleNewBondedAccount, + useRef(document) ); + return ( { const result: Exposure[] = []; let i = 0; - // NOTE: Only one page is fetched for each validator for now. for (const pages of pagedResults) { + // NOTE: Only one page is fetched for each validator for now. const page = pages[0]; const { diff --git a/packages/app/src/controllers/Subscriptions/types.ts b/packages/app/src/controllers/Subscriptions/types.ts index c050a3fc7..afcf10324 100644 --- a/packages/app/src/controllers/Subscriptions/types.ts +++ b/packages/app/src/controllers/Subscriptions/types.ts @@ -3,6 +3,7 @@ import type { ActiveEra } from 'model/Subscribe/ActiveEra'; import type { BlockNumber } from 'model/Subscribe/BlockNumber'; +import type { Bonded } from 'model/Subscribe/Bonded'; import type { NetworkMetrics } from 'model/Subscribe/NetworkMetrics'; import type { PoolsConfig } from 'model/Subscribe/PoolsConfig'; import type { StakingMetrics } from 'model/Subscribe/StakingMetrics'; @@ -11,6 +12,7 @@ import type { StakingMetrics } from 'model/Subscribe/StakingMetrics'; export type Subscription = | ActiveEra | BlockNumber + | Bonded | NetworkMetrics | PoolsConfig | StakingMetrics; diff --git a/packages/app/src/model/Subscribe/Bonded/index.tsx b/packages/app/src/model/Subscribe/Bonded/index.tsx new file mode 100644 index 000000000..7e96e7780 --- /dev/null +++ b/packages/app/src/model/Subscribe/Bonded/index.tsx @@ -0,0 +1,64 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { BondedAccount } from 'contexts/Bonded/types'; +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import type { NetworkName } from 'types'; + +export class Bonded implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // The stash address. + #address: string; + + // The bonded address. + bonded: string; + + // Active subscription. + #sub: Subscription; + + constructor(network: NetworkName, address: string) { + this.#network = network; + this.#address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const unsub = pApi.query.Staking.Bonded.watchValue( + this.#address + ).subscribe((controller) => { + const account: BondedAccount = { + address: this.#address, + bonded: controller || null, + }; + + // Send bonded account to UI. + document.dispatchEvent( + new CustomEvent('new-bonded-account', { + detail: { + account, + }, + }) + ); + }); + this.#sub = unsub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index e909ff810..f27bb185b 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -20,6 +20,7 @@ import type { APIEventDetail, PapiReadyEvent } from 'model/Api/types'; import type { OnlineStatusEvent } from 'controllers/OnlineStatus/types'; import type { AnyJson } from '@w3ux/types'; import type { BlockNumberEventDetail } from 'model/Subscribe/BlockNumber/types'; +import type { BondedAccount } from 'contexts/Bonded/types'; declare global { interface Window { @@ -42,6 +43,7 @@ declare global { stakingMetrics: APIStakingMetrics; }>; 'new-active-pool': CustomEvent; + 'new-bonded-account': CustomEvent; 'new-sync-status': CustomEvent; 'new-external-account': CustomEvent<{ address: string }>; 'new-account-balance': CustomEvent; From b1be4e34f49a24c29abf6e17a4ae38e8ae7184e0 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 17 Nov 2024 14:19:32 +0700 Subject: [PATCH 24/84] use `substrate-bindings` for pool address generation --- .../src/hooks/useCreatePoolAccounts/index.tsx | 34 +++++++++---------- packages/consts/src/index.ts | 9 ----- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/packages/app/src/hooks/useCreatePoolAccounts/index.tsx b/packages/app/src/hooks/useCreatePoolAccounts/index.tsx index e3bdc79da..c7285010d 100644 --- a/packages/app/src/hooks/useCreatePoolAccounts/index.tsx +++ b/packages/app/src/hooks/useCreatePoolAccounts/index.tsx @@ -1,14 +1,17 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { bnToU8a, u8aConcat } from '@polkadot/util'; +import { bnToU8a, stringToU8a, u8aConcat } from '@polkadot/util'; import BigNumber from 'bignumber.js'; import { BN } from 'bn.js'; -import { EmptyH256, ModPrefix, U32Opts } from 'consts'; import { useApi } from 'contexts/Api'; +import { AccountId } from '@polkadot-api/substrate-bindings'; export const useCreatePoolAccounts = () => { - const { api, consts } = useApi(); + const { + consts, + chainSpecs: { ss58Format }, + } = useApi(); const { poolsPalletId } = consts; // Generates pool stash and reward accounts. Assumes `poolsPalletId` is synced. @@ -21,21 +24,16 @@ export const useCreatePoolAccounts = () => { }; const createAccount = (poolId: BigNumber, index: number): string => { - if (!api) { - return ''; - } - return api.registry - .createType( - 'AccountId32', - u8aConcat( - ModPrefix, - poolsPalletId, - new Uint8Array([index]), - bnToU8a(new BN(poolId.toString()), U32Opts), - EmptyH256 - ) - ) - .toString(); + const key = u8aConcat( + stringToU8a('modl'), + poolsPalletId, + new Uint8Array([index]), + bnToU8a(new BN(poolId.toString()), { bitLength: 32, isLe: true }), + new Uint8Array(32) + ); + + const codec = AccountId(ss58Format); + return codec.dec(key); }; return createPoolAccounts; diff --git a/packages/consts/src/index.ts b/packages/consts/src/index.ts index 82fd0d258..92c9d1cd8 100644 --- a/packages/consts/src/index.ts +++ b/packages/consts/src/index.ts @@ -1,8 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { stringToU8a } from '@polkadot/util'; - /* * Global Constants */ @@ -11,13 +9,6 @@ export const ManualSigners = ['ledger', 'vault', 'wallet_connect']; export const DiscordSupportUrl = 'https://discord.gg/QY7CSSJm3D'; export const MailSupportAddress = 'staking@polkadot.cloud'; -/* - * Byte Helpers - */ -export const EmptyH256 = new Uint8Array(32); -export const ModPrefix = stringToU8a('modl'); -export const U32Opts = { bitLength: 32, isLe: true }; - /* * Element Thresholds */ From 953392864d9c5d82380bbb374d801d070adec756 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 10:58:02 +0700 Subject: [PATCH 25/84] add `FastUnstakeConfig` subscription --- .../app/src/contexts/FastUnstake/defaults.ts | 4 +- .../app/src/contexts/FastUnstake/index.tsx | 68 ++++++++++--------- .../app/src/contexts/FastUnstake/types.ts | 7 +- .../src/controllers/Subscriptions/types.ts | 2 + .../src/modals/ManageFastUnstake/index.tsx | 4 +- .../Subscribe/FastUnstakeConfig/index.ts | 57 ++++++++++++++++ .../Subscribe/FastUnstakeConfig/types.ts | 12 ++++ 7 files changed, 115 insertions(+), 39 deletions(-) create mode 100644 packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts create mode 100644 packages/app/src/model/Subscribe/FastUnstakeConfig/types.ts diff --git a/packages/app/src/contexts/FastUnstake/defaults.ts b/packages/app/src/contexts/FastUnstake/defaults.ts index 3002dce83..7b950e2c5 100644 --- a/packages/app/src/contexts/FastUnstake/defaults.ts +++ b/packages/app/src/contexts/FastUnstake/defaults.ts @@ -14,6 +14,6 @@ export const defaultFastUnstakeContext: FastUnstakeContextInterface = { meta: defaultMeta, isExposed: null, queueDeposit: null, - head: null, - counterForQueue: null, + head: undefined, + counterForQueue: undefined, }; diff --git a/packages/app/src/contexts/FastUnstake/index.tsx b/packages/app/src/contexts/FastUnstake/index.tsx index 4d950c642..3f090454e 100644 --- a/packages/app/src/contexts/FastUnstake/index.tsx +++ b/packages/app/src/contexts/FastUnstake/index.tsx @@ -20,6 +20,11 @@ import type { MetaInterface, } from './types'; import type { AnyJson } from '@w3ux/types'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { FastUnstakeConfig } from 'model/Subscribe/FastUnstakeConfig'; +import { useEventListener } from 'usehooks-ts'; +import { isCustomEvent } from 'controllers/utils'; +import type { FastUnstakeHead } from 'model/Subscribe/FastUnstakeConfig/types'; const worker = new Worker(); @@ -61,12 +66,10 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { const queueDepositRef = useRef(queueDeposit); // store fastUnstake head. - const [head, setHead] = useState(null); - const headRef = useRef(head); + const [head, setHead] = useState(); // store fastUnstake counter for queue. - const [counterForQueue, setCounterForQueue] = useState(null); - const counterForQueueRef = useRef(counterForQueue); + const [counterForQueue, setCounterForQueue] = useState(); // store fastUnstake subscription unsub. const unsubs = useRef([]); @@ -77,11 +80,10 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // check until bond duration eras surpasssed. const checkToEra = activeEra.index.minus(bondDuration); - // Reset state on network or active account change. + // Reset state on active account change. useEffect(() => { setStateWithRef(false, setChecking, checkingRef); setStateWithRef(null, setqueueDeposit, queueDepositRef); - setStateWithRef(null, setCounterForQueue, counterForQueueRef); setStateWithRef(null, setIsExposed, isExposedRef); setStateWithRef(defaultMeta, setMeta, metaRef); unsubs.current = []; @@ -90,9 +92,14 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { for (const unsub of unsubs.current) { unsub(); } + }, [activeAccount]); - // Resubscribe to fast unstake queue. - }, [activeAccount, network]); + // Reset state on network change. + useEffect(() => { + setHead(undefined); + setCounterForQueue(undefined); + unsubs.current = []; + }, [network]); // Subscribe to fast unstake queue as soon as api is ready. useEffect(() => { @@ -272,7 +279,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { return; } - // TODO: Make a `combineLatest` subscription with new event ---------------------- + // TODO: Make a subscription with new event ---------------------- const subscribeQueue = async (a: MaybeAddress) => { const u = await api.query.fastUnstake.queue(a, (q: AnyApi) => setStateWithRef( @@ -283,32 +290,16 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { ); return u; }; - const subscribeHead = async () => { - const u = await api.query.fastUnstake.head((result: AnyApi) => { - const h = result.unwrapOrDefault(null).toHuman(); - setStateWithRef(h, setHead, headRef); - }); - return u; - }; - const subscribeCounterForQueue = async () => { - const u = await api.query.fastUnstake.counterForQueue( - (result: AnyApi) => { - const c = result.toHuman(); - setStateWithRef(c, setCounterForQueue, counterForQueueRef); - } - ); - return u; - }; - // -------------------------------------------------------------------------------- + // ---------------------------------------------------------------- - // Subscribe to queue + head. + SubscriptionsController.set( + network, + 'fastUnstakeMeta', + new FastUnstakeConfig(network) + ); // initiate subscription, add to unsubs. - await Promise.all([ - subscribeQueue(activeAccount), - subscribeHead(), - subscribeCounterForQueue(), - ]).then((u) => { + await Promise.all([subscribeQueue(activeAccount)]).then((u) => { unsubs.current = u; }); }; @@ -337,6 +328,19 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { return localMetaValidated; }; + // Handle fast unstake meta events. + const handleNewFastUnstakeData = (e: Event) => { + if (isCustomEvent(e)) { + const { head: eventHead, counterForQueue: eventCounterForQueue } = + e.detail; + setHead(eventHead); + setCounterForQueue(eventCounterForQueue); + } + }; + + const documentRef = useRef(document); + useEventListener('api-status', handleNewFastUnstakeData, documentRef); + return ( { {t('fastUnstakeOnceRegistered')}

- {t('fastUnstakeCurrentQueue')}: {counterForQueue} + {t('fastUnstakeCurrentQueue')}: {counterForQueue || 0}

@@ -188,7 +188,7 @@ export const ManageFastUnstake = () => {

- {t('fastUnstakeCurrentQueue')}: {counterForQueue} + {t('fastUnstakeCurrentQueue')}: {counterForQueue || 0}

{t('fastUnstakeUnorderedNote')}

diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts new file mode 100644 index 000000000..b6af21863 --- /dev/null +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts @@ -0,0 +1,57 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; +import type { NetworkName } from 'types'; +import type { FastUnstakeConfigResult } from './types'; + +export class FastUnstakeConfig implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // Active subscription. + #sub: Subscription; + + constructor(network: NetworkName) { + this.#network = network; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const sub = combineLatest([ + pApi.query.FastUnstake.Head.watchValue(), + pApi.query.FastUnstake.CounterForQueue.watchValue(), + ]).subscribe(([head, counterForQueue]) => { + const data: FastUnstakeConfigResult = { + head, + counterForQueue, + }; + + document.dispatchEvent( + new CustomEvent('new-fast-unstake-data', { + detail: { data }, + }) + ); + }); + + this.#sub = sub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/types.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/types.ts new file mode 100644 index 000000000..8d64f3c59 --- /dev/null +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/types.ts @@ -0,0 +1,12 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export interface FastUnstakeConfigResult { + head: FastUnstakeHead; + counterForQueue: number; +} + +export interface FastUnstakeHead { + stashes: [string, bigint][]; + checked: number[]; +} From 40d9dbe7788747bf9848bb004d07699988944237 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 10:59:16 +0700 Subject: [PATCH 26/84] fix --- packages/app/src/contexts/FastUnstake/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/app/src/contexts/FastUnstake/index.tsx b/packages/app/src/contexts/FastUnstake/index.tsx index 3f090454e..6f1c78fe4 100644 --- a/packages/app/src/contexts/FastUnstake/index.tsx +++ b/packages/app/src/contexts/FastUnstake/index.tsx @@ -339,7 +339,11 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { }; const documentRef = useRef(document); - useEventListener('api-status', handleNewFastUnstakeData, documentRef); + useEventListener( + 'new-fast-unstake-data', + handleNewFastUnstakeData, + documentRef + ); return ( Date: Mon, 18 Nov 2024 10:59:34 +0700 Subject: [PATCH 27/84] add type --- packages/app/src/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index f27bb185b..9472a4b96 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -21,6 +21,7 @@ import type { OnlineStatusEvent } from 'controllers/OnlineStatus/types'; import type { AnyJson } from '@w3ux/types'; import type { BlockNumberEventDetail } from 'model/Subscribe/BlockNumber/types'; import type { BondedAccount } from 'contexts/Bonded/types'; +import type { FastUnstakeConfigResult } from 'model/Subscribe/FastUnstakeConfig/types'; declare global { interface Window { @@ -43,6 +44,7 @@ declare global { stakingMetrics: APIStakingMetrics; }>; 'new-active-pool': CustomEvent; + 'new-fast-unstake-data': CustomEvent; 'new-bonded-account': CustomEvent; 'new-sync-status': CustomEvent; 'new-external-account': CustomEvent<{ address: string }>; From 71cd4e53bc35f651bb17d75fcefd92c7a18f52c6 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 11:03:59 +0700 Subject: [PATCH 28/84] rename --- packages/app/src/contexts/FastUnstake/index.tsx | 2 +- packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts | 2 +- packages/app/src/types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/contexts/FastUnstake/index.tsx b/packages/app/src/contexts/FastUnstake/index.tsx index 6f1c78fe4..de5b7fda4 100644 --- a/packages/app/src/contexts/FastUnstake/index.tsx +++ b/packages/app/src/contexts/FastUnstake/index.tsx @@ -340,7 +340,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { const documentRef = useRef(document); useEventListener( - 'new-fast-unstake-data', + 'new-fast-unstake-config', handleNewFastUnstakeData, documentRef ); diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts index b6af21863..e3855f57f 100644 --- a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts @@ -35,7 +35,7 @@ export class FastUnstakeConfig implements Unsubscribable { }; document.dispatchEvent( - new CustomEvent('new-fast-unstake-data', { + new CustomEvent('new-fast-unstake-config', { detail: { data }, }) ); diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index 9472a4b96..77fe762ff 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -44,7 +44,7 @@ declare global { stakingMetrics: APIStakingMetrics; }>; 'new-active-pool': CustomEvent; - 'new-fast-unstake-data': CustomEvent; + 'new-fast-unstake-config': CustomEvent; 'new-bonded-account': CustomEvent; 'new-sync-status': CustomEvent; 'new-external-account': CustomEvent<{ address: string }>; From c11b18f9520c759463e90203096ebaac72a3f27b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 11:06:45 +0700 Subject: [PATCH 29/84] polish --- .../app/src/model/Subscribe/FastUnstakeConfig/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts index e3855f57f..94237a35f 100644 --- a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts @@ -15,6 +15,8 @@ export class FastUnstakeConfig implements Unsubscribable { // Active subscription. #sub: Subscription; + config: FastUnstakeConfigResult; + constructor(network: NetworkName) { this.#network = network; this.subscribe(); @@ -29,14 +31,16 @@ export class FastUnstakeConfig implements Unsubscribable { pApi.query.FastUnstake.Head.watchValue(), pApi.query.FastUnstake.CounterForQueue.watchValue(), ]).subscribe(([head, counterForQueue]) => { - const data: FastUnstakeConfigResult = { + const config: FastUnstakeConfigResult = { head, counterForQueue, }; + this.config = config; + document.dispatchEvent( new CustomEvent('new-fast-unstake-config', { - detail: { data }, + detail: { ...config }, }) ); }); From f65b7c7fe2daae6136cc793c0a6e990060ee5233 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 11:36:50 +0700 Subject: [PATCH 30/84] add `FastUnstakeQueue` subscription, rm from context --- .../app/src/contexts/FastUnstake/defaults.ts | 2 +- .../app/src/contexts/FastUnstake/index.tsx | 89 ++++++++----------- .../app/src/contexts/FastUnstake/types.ts | 12 ++- .../src/controllers/Subscriptions/types.ts | 2 + packages/app/src/hooks/useUnstaking/index.tsx | 2 +- .../model/Subscribe/FastUnstakeQueue/index.ts | 60 +++++++++++++ packages/app/src/types.ts | 2 + 7 files changed, 114 insertions(+), 55 deletions(-) create mode 100644 packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts diff --git a/packages/app/src/contexts/FastUnstake/defaults.ts b/packages/app/src/contexts/FastUnstake/defaults.ts index 7b950e2c5..d1f39320c 100644 --- a/packages/app/src/contexts/FastUnstake/defaults.ts +++ b/packages/app/src/contexts/FastUnstake/defaults.ts @@ -13,7 +13,7 @@ export const defaultFastUnstakeContext: FastUnstakeContextInterface = { checking: false, meta: defaultMeta, isExposed: null, - queueDeposit: null, head: undefined, + queueDeposit: undefined, counterForQueue: undefined, }; diff --git a/packages/app/src/contexts/FastUnstake/index.tsx b/packages/app/src/contexts/FastUnstake/index.tsx index de5b7fda4..2d02f9d26 100644 --- a/packages/app/src/contexts/FastUnstake/index.tsx +++ b/packages/app/src/contexts/FastUnstake/index.tsx @@ -1,13 +1,13 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { rmCommas, setStateWithRef } from '@w3ux/utils'; +import { setStateWithRef } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; import { useApi } from 'contexts/Api'; import { useStaking } from 'contexts/Staking'; -import type { AnyApi, MaybeAddress } from 'types'; +import type { MaybeAddress } from 'types'; import Worker from 'workers/stakers?worker'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { validateLocalExposure } from 'contexts/Validators/Utils'; @@ -16,6 +16,7 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { defaultFastUnstakeContext, defaultMeta } from './defaults'; import type { FastUnstakeContextInterface, + FastUnstakeQueueDeposit, LocalMeta, MetaInterface, } from './types'; @@ -25,6 +26,8 @@ import { FastUnstakeConfig } from 'model/Subscribe/FastUnstakeConfig'; import { useEventListener } from 'usehooks-ts'; import { isCustomEvent } from 'controllers/utils'; import type { FastUnstakeHead } from 'model/Subscribe/FastUnstakeConfig/types'; +import { FastUnstakeQueue } from 'model/Subscribe/FastUnstakeQueue'; +import { ApiController } from 'controllers/Api'; const worker = new Worker(); @@ -39,7 +42,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { const { activeAccount } = useActiveAccounts(); const { inSetup, fetchEraStakers, isBonding } = useStaking(); const { - api, consts, isReady, activeEra, @@ -62,8 +64,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { const metaRef = useRef(meta); // store fastUnstake queue deposit for user. - const [queueDeposit, setqueueDeposit] = useState(null); - const queueDepositRef = useRef(queueDeposit); + const [queueDeposit, setQueueDeposit] = useState(); // store fastUnstake head. const [head, setHead] = useState(); @@ -71,9 +72,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // store fastUnstake counter for queue. const [counterForQueue, setCounterForQueue] = useState(); - // store fastUnstake subscription unsub. - const unsubs = useRef([]); - // localStorage key to fetch local metadata. const getLocalkey = (a: MaybeAddress) => `${network}_fast_unstake_${a}`; @@ -82,15 +80,21 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // Reset state on active account change. useEffect(() => { + // Reset fast unstake managment state. + setQueueDeposit(undefined); setStateWithRef(false, setChecking, checkingRef); - setStateWithRef(null, setqueueDeposit, queueDepositRef); setStateWithRef(null, setIsExposed, isExposedRef); setStateWithRef(defaultMeta, setMeta, metaRef); - unsubs.current = []; - // cancel fast unstake check on network change or account change. - for (const unsub of unsubs.current) { - unsub(); + // Re-subscribe to fast unstake queue. + SubscriptionsController.remove(network, 'fastUnstakeQueue'); + + if (activeAccount) { + SubscriptionsController.set( + network, + 'fastUnstakeQueue', + new FastUnstakeQueue(network, activeAccount) + ); } }, [activeAccount]); @@ -98,13 +102,12 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { useEffect(() => { setHead(undefined); setCounterForQueue(undefined); - unsubs.current = []; }, [network]); // Subscribe to fast unstake queue as soon as api is ready. useEffect(() => { if (isReady) { - subscribeToFastUnstakeQueue(); + subscribeToFastUnstakeMeta(); } }, [isReady]); @@ -159,12 +162,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { processEligibility(activeAccount, maybeNextEra); } } - - return () => { - for (const unsub of unsubs.current) { - unsub(); - } - }; }, [ inSetup(), isReady, @@ -218,7 +215,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { if (exposed) { // Account is exposed - stop checking. - // cancel checking and update exposed state. setStateWithRef(false, setChecking, checkingRef); setStateWithRef(true, setIsExposed, isExposedRef); @@ -226,9 +222,8 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // successfully checked current era - bondDuration eras. setStateWithRef(false, setChecking, checkingRef); setStateWithRef(false, setIsExposed, isExposedRef); - - // Finished, not exposed. } else { + // Finished, not exposed. // continue checking the next era. checkEra(new BigNumber(era).minus(1)); } @@ -241,7 +236,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { if ( era.isLessThan(0) || !bondDuration.isGreaterThan(0) || - !api || !a || checkingRef.current || !activeAccount || @@ -256,10 +250,6 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // calls service worker to check exppsures for given era. const checkEra = async (era: BigNumber) => { - if (!api) { - return; - } - const exposures = await fetchEraStakers(era.toString()); worker.postMessage({ @@ -274,34 +264,16 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { }; // subscribe to fastUnstake queue - const subscribeToFastUnstakeQueue = async () => { - if (!api) { + const subscribeToFastUnstakeMeta = async () => { + const { pApi } = await ApiController.get(network); + if (!pApi) { return; } - - // TODO: Make a subscription with new event ---------------------- - const subscribeQueue = async (a: MaybeAddress) => { - const u = await api.query.fastUnstake.queue(a, (q: AnyApi) => - setStateWithRef( - new BigNumber(rmCommas(q.unwrapOrDefault(0).toString())), - setqueueDeposit, - queueDepositRef - ) - ); - return u; - }; - // ---------------------------------------------------------------- - SubscriptionsController.set( network, 'fastUnstakeMeta', new FastUnstakeConfig(network) ); - - // initiate subscription, add to unsubs. - await Promise.all([subscribeQueue(activeAccount)]).then((u) => { - unsubs.current = u; - }); }; // gets any existing fast unstake metadata for an account. @@ -329,7 +301,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { }; // Handle fast unstake meta events. - const handleNewFastUnstakeData = (e: Event) => { + const handleNewFastUnstakeConfig = (e: Event) => { if (isCustomEvent(e)) { const { head: eventHead, counterForQueue: eventCounterForQueue } = e.detail; @@ -338,10 +310,23 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { } }; + // Handle fast unstake deposit events. + const handleNewFastUnstakeDeposit = (e: Event) => { + if (isCustomEvent(e)) { + const { address, deposit } = e.detail; + setQueueDeposit({ address, deposit: new BigNumber(deposit.toString()) }); + } + }; + const documentRef = useRef(document); useEventListener( 'new-fast-unstake-config', - handleNewFastUnstakeData, + handleNewFastUnstakeConfig, + documentRef + ); + useEventListener( + 'new-fast-unstake-deposit', + handleNewFastUnstakeDeposit, documentRef ); diff --git a/packages/app/src/contexts/FastUnstake/types.ts b/packages/app/src/contexts/FastUnstake/types.ts index bf9c4c556..345808b4a 100644 --- a/packages/app/src/contexts/FastUnstake/types.ts +++ b/packages/app/src/contexts/FastUnstake/types.ts @@ -18,7 +18,17 @@ export interface FastUnstakeContextInterface { checking: boolean; meta: MetaInterface; isExposed: boolean | null; - queueDeposit: BigNumber | null; + queueDeposit: FastUnstakeQueueDeposit | undefined; head: FastUnstakeHead | undefined; counterForQueue: number | undefined; } + +export interface FastUnstakeQueueDeposit { + address: string; + deposit: BigNumber; +} + +export interface FastUnstakeQueueResult { + address: string; + deposit: bigint; +} diff --git a/packages/app/src/controllers/Subscriptions/types.ts b/packages/app/src/controllers/Subscriptions/types.ts index 91b94446a..d835634a5 100644 --- a/packages/app/src/controllers/Subscriptions/types.ts +++ b/packages/app/src/controllers/Subscriptions/types.ts @@ -5,6 +5,7 @@ import type { ActiveEra } from 'model/Subscribe/ActiveEra'; import type { BlockNumber } from 'model/Subscribe/BlockNumber'; import type { Bonded } from 'model/Subscribe/Bonded'; import type { FastUnstakeConfig } from 'model/Subscribe/FastUnstakeConfig'; +import type { FastUnstakeQueue } from 'model/Subscribe/FastUnstakeQueue'; import type { NetworkMetrics } from 'model/Subscribe/NetworkMetrics'; import type { PoolsConfig } from 'model/Subscribe/PoolsConfig'; import type { StakingMetrics } from 'model/Subscribe/StakingMetrics'; @@ -15,6 +16,7 @@ export type Subscription = | BlockNumber | Bonded | FastUnstakeConfig + | FastUnstakeQueue | NetworkMetrics | PoolsConfig | StakingMetrics; diff --git a/packages/app/src/hooks/useUnstaking/index.tsx b/packages/app/src/hooks/useUnstaking/index.tsx index 3a245cff0..5980cf16a 100644 --- a/packages/app/src/hooks/useUnstaking/index.tsx +++ b/packages/app/src/hooks/useUnstaking/index.tsx @@ -28,7 +28,7 @@ export const useUnstaking = () => { // determine if user is fast unstaking. const inHead = head?.stashes.find((s: AnyJson) => s[0] === activeAccount) ?? undefined; - const inQueue = queueDeposit?.isGreaterThan(0) ?? false; + const inQueue = queueDeposit?.deposit?.isGreaterThan(0) ?? false; const registered = inHead || inQueue; diff --git a/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts new file mode 100644 index 000000000..156b4e2a4 --- /dev/null +++ b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts @@ -0,0 +1,60 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; +import type { NetworkName } from 'types'; + +export class FastUnstakeQueue implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // The depositor address. + #address: string; + + // The deposit. + queue: bigint; + + // Active subscription. + #sub: Subscription; + + constructor(network: NetworkName, address: string) { + this.#network = network; + this.#address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const unsub = pApi.query.FastUnstake.Queue.watchValue( + this.#address + ).subscribe((queue) => { + this.queue = queue; + + document.dispatchEvent( + new CustomEvent('new-fast-unstake-deposit', { + detail: { + address: this.#address, + deposit: queue || 0n, + }, + }) + ); + }); + this.#sub = unsub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index 77fe762ff..1651a9343 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -22,6 +22,7 @@ import type { AnyJson } from '@w3ux/types'; import type { BlockNumberEventDetail } from 'model/Subscribe/BlockNumber/types'; import type { BondedAccount } from 'contexts/Bonded/types'; import type { FastUnstakeConfigResult } from 'model/Subscribe/FastUnstakeConfig/types'; +import type { FastUnstakeQueueResult } from 'contexts/FastUnstake/types'; declare global { interface Window { @@ -45,6 +46,7 @@ declare global { }>; 'new-active-pool': CustomEvent; 'new-fast-unstake-config': CustomEvent; + 'new-fast-unstake-deposit': CustomEvent; 'new-bonded-account': CustomEvent; 'new-sync-status': CustomEvent; 'new-external-account': CustomEvent<{ address: string }>; From 969409c247b6f4bc0b3a8257225c18d406cd1b3d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 11:38:42 +0700 Subject: [PATCH 31/84] rm log --- packages/app/src/model/Api/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index f8c15e9d4..d65f6088f 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -206,7 +206,6 @@ export class Api { // Dispatch 'papi-ready' event to let contexts populate constants. this.dispatchPapiReadyEvent(); } catch (e) { - console.debug('PAPI chain spec failed'); // TODO: Expand this when PJS API has been removed. Flag an error if there are any issues // bootstrapping chain spec. NOTE: This can happen when PAPI is the standalone connection // method. From 2af9b149349ef3c812d05d521cc2fa97a3b2f24c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 14:03:25 +0700 Subject: [PATCH 32/84] Abstract balances to `AccountBalances` subscription --- packages/app/src/contexts/Balances/index.tsx | 10 +- packages/app/src/contexts/Balances/types.ts | 6 +- .../Connect/ImportedAccounts/index.tsx | 3 +- .../app/src/contexts/FastUnstake/index.tsx | 2 +- .../app/src/controllers/Balances/index.ts | 304 ++---------------- .../src/controllers/Subscriptions/types.ts | 2 + .../app/src/hooks/useActiveBalances/index.tsx | 5 +- .../model/Subscribe/AccountBalances/index.ts | 232 +++++++++++++ 8 files changed, 285 insertions(+), 279 deletions(-) create mode 100644 packages/app/src/model/Subscribe/AccountBalances/index.ts diff --git a/packages/app/src/contexts/Balances/index.tsx b/packages/app/src/contexts/Balances/index.tsx index 110b8dfed..ab8087b0a 100644 --- a/packages/app/src/contexts/Balances/index.tsx +++ b/packages/app/src/contexts/Balances/index.tsx @@ -17,6 +17,7 @@ import { SyncController } from 'controllers/Sync'; import { useApi } from 'contexts/Api'; import { ActivePoolsController } from 'controllers/ActivePools'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; +import { useNetwork } from 'contexts/Network'; export const BalancesContext = createContext( defaults.defaultBalancesContext @@ -25,6 +26,7 @@ export const BalancesContext = createContext( export const useBalances = () => useContext(BalancesContext); export const BalancesProvider = ({ children }: { children: ReactNode }) => { + const { network } = useNetwork(); const { getBondedAccount } = useBonded(); const { accounts } = useImportedAccounts(); const createPoolAccounts = useCreatePoolAccounts(); @@ -82,7 +84,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // Check whether all accounts have been synced and update state accordingly. const checkBalancesSynced = () => { - if (Object.keys(BalancesController.balances).length === accounts.length) { + if (Object.keys(BalancesController.accounts).length === accounts.length) { SyncController.dispatch('balances', 'complete'); } }; @@ -91,7 +93,11 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // payload. const getNonce = (address: MaybeAddress) => { if (address) { - const maybeNonce = BalancesController.balances[address]?.nonce; + const accountBalances = BalancesController.getAccountBalances( + network, + address + ); + const maybeNonce = accountBalances?.balances?.nonce; if (maybeNonce) { return maybeNonce; } diff --git a/packages/app/src/contexts/Balances/types.ts b/packages/app/src/contexts/Balances/types.ts index 66fd06ff0..3031e8dd3 100644 --- a/packages/app/src/contexts/Balances/types.ts +++ b/packages/app/src/contexts/Balances/types.ts @@ -20,10 +20,10 @@ export interface BalancesContextInterface { export type ActiveBalancesState = Record; export interface ActiveBalance { - ledger: Ledger; + ledger: Ledger | undefined; balances: Balances; - payee: PayeeConfig; - poolMembership: PoolMembership; + payee: PayeeConfig | undefined; + poolMembership: PoolMembership | undefined; nominations: Nominations; } diff --git a/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx b/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx index 9c837f77e..bac80eb17 100644 --- a/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx +++ b/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx @@ -40,7 +40,6 @@ export const ImportedAccountsProvider = ({ const { otherAccounts } = useOtherAccounts(); const { getExtensionAccounts } = useExtensionAccounts(); const { setActiveAccount, setActiveProxy } = useActiveAccounts(); - // Get the imported extension accounts formatted with the current network's ss58 prefix. const extensionAccounts = getExtensionAccounts(ss58); @@ -118,7 +117,7 @@ export const ImportedAccountsProvider = ({ useEffectIgnoreInitial(() => { if (api && isReady) { BalancesController.syncAccounts( - api, + network, allAccounts.map((a) => a.address) ); } diff --git a/packages/app/src/contexts/FastUnstake/index.tsx b/packages/app/src/contexts/FastUnstake/index.tsx index 2d02f9d26..76f0627f3 100644 --- a/packages/app/src/contexts/FastUnstake/index.tsx +++ b/packages/app/src/contexts/FastUnstake/index.tsx @@ -265,7 +265,7 @@ export const FastUnstakeProvider = ({ children }: { children: ReactNode }) => { // subscribe to fastUnstake queue const subscribeToFastUnstakeMeta = async () => { - const { pApi } = await ApiController.get(network); + const { pApi } = ApiController.get(network); if (!pApi) { return; } diff --git a/packages/app/src/controllers/Balances/index.ts b/packages/app/src/controllers/Balances/index.ts index df8086d5f..3010ef2f2 100644 --- a/packages/app/src/controllers/Balances/index.ts +++ b/packages/app/src/controllers/Balances/index.ts @@ -1,61 +1,23 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { rmCommas } from '@w3ux/utils'; -import BigNumber from 'bignumber.js'; -import type { AnyApi, MaybeAddress } from 'types'; -import type { - ActiveBalance, - Balances, - Ledger, - Nominations, - UnlockChunkRaw, -} from 'contexts/Balances/types'; -import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; -import type { PoolMembership } from 'contexts/Pools/types'; +import type { NetworkName } from 'types'; +import type { ActiveBalance } from 'contexts/Balances/types'; import { SyncController } from 'controllers/Sync'; -import { defaultNominations } from './defaults'; -import type { VoidFn } from '@polkadot/api/types'; -import type { ApiPromise } from '@polkadot/api'; -import { stringToBn } from 'library/Utils'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { AccountBalances } from 'model/Subscribe/AccountBalances'; export class BalancesController { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // Accounts that are being subscribed to. static accounts: string[] = []; - // Account ledgers, populated by api callbacks. - static ledgers: Record = {}; - - // Account balances, populated by api callbacks. - static balances: Record = {}; - - // Account payees, populated by api callbacks. - static payees: Record = {}; - - // Account pool membership and claim commissions, populated by api callbacks. - static poolMemberships: Record = {}; - - // Account nominations, populated by api callbacks. - static nominations: Record = {}; - - // Unsubscribe objects. - static #unsubs: Record = {}; - - // ------------------------------------------------------ - // Account syncing. - // ------------------------------------------------------ - // Subscribes new accounts and unsubscribes & removes removed accounts. static syncAccounts = async ( - api: ApiPromise, + network: NetworkName, newAccounts: string[] ): Promise => { // Handle accounts that have been removed. - this.handleRemovedAccounts(newAccounts); + this.handleRemovedAccounts(network, newAccounts); // Determine new accounts that need to be subscribed to. const accountsAdded = newAccounts.filter( @@ -73,237 +35,55 @@ export class BalancesController { // Subscribe to and add new accounts data. accountsAdded.forEach(async (address) => { this.accounts.push(address); - const unsub = await api.queryMulti( - [ - [api.query.staking.ledger, address], - [api.query.system.account, address], - [api.query.balances.locks, address], - [api.query.staking.payee, address], - [api.query.nominationPools.poolMembers, address], - [api.query.nominationPools.claimPermissions, address], - [api.query.staking.nominators, address], - ], - async ([ - ledgerResult, - accountResult, - locksResult, - payeeResult, - poolMembersResult, - claimPermissionsResult, - nominatorsResult, - ]): Promise => { - this.handleLedgerCallback(address, ledgerResult); - this.handleAccountCallback(address, accountResult, locksResult); - this.handlePayeeCallback(address, payeeResult); - - // NOTE: async: contains runtime call for pending rewards. - await this.handlePoolMembershipCallback( - api, - address, - poolMembersResult, - claimPermissionsResult - ); - this.handleNominations(address, nominatorsResult); - - // Send updated account state back to UI. - document.dispatchEvent( - new CustomEvent('new-account-balance', { - detail: { - address, - ledger: this.ledgers[address], - balances: this.balances[address], - payee: this.payees[address], - poolMembership: this.poolMemberships[address], - nominations: this.nominations[address], - }, - }) - ); - } + SubscriptionsController.set( + network, + `accountBalances-${address}`, + new AccountBalances(network, address) ); - this.#unsubs[address] = unsub; }); }; // Remove accounts that no longer exist. - static handleRemovedAccounts = (newAccounts: string[]): void => { + static handleRemovedAccounts = ( + network: NetworkName, + newAccounts: string[] + ): void => { // Determine removed accounts. const accountsRemoved = this.accounts.filter( (account) => !newAccounts.includes(account) ); // Unsubscribe from removed account subscriptions. accountsRemoved.forEach((account) => { - if (this.#unsubs[account]) { - this.#unsubs[account](); - } - delete this.#unsubs[account]; - delete this.ledgers[account]; - delete this.balances[account]; - delete this.payees[account]; - delete this.poolMemberships[account]; - delete this.nominations[account]; + SubscriptionsController.remove(network, `accountBalances-${account}`); }); + // Remove removed accounts from class. this.accounts = this.accounts.filter( (account) => !accountsRemoved.includes(account) ); }; - // Handle ledger callback. - static handleLedgerCallback = (address: string, result: AnyApi): void => { - const ledger = result.unwrapOr(null); - - // If ledger is null, remove from class data and exit early. - if (ledger === null) { - delete this.ledgers[address]; - return; - } - - const { stash, total, active, unlocking } = ledger; - - // Send stash address to UI as event if not presently imported. - if (!this.accounts.includes(stash.toString())) { - document.dispatchEvent( - new CustomEvent('new-external-account', { - detail: { address: stash.toString() }, - }) - ); - } - - this.ledgers[address] = { - stash: stash.toString(), - active: stringToBn(active.toString()), - total: stringToBn(total.toString()), - unlocking: unlocking.toHuman().map(({ era, value }: UnlockChunkRaw) => ({ - era: Number(rmCommas(era)), - value: stringToBn(value), - })), - }; - }; - - // Handle account callback. - static handleAccountCallback = ( - address: string, - { data: accountData, nonce }: AnyApi, - locksResult: AnyApi - ): void => { - this.balances[address] = { - nonce: nonce.toNumber(), - balance: { - free: stringToBn(accountData.free.toString()), - reserved: stringToBn(accountData.reserved.toString()), - frozen: stringToBn(accountData.frozen.toString()), - }, - locks: locksResult - .toHuman() - .map((lock: { id: string; amount: string }) => ({ - ...lock, - id: lock.id.trim(), - amount: stringToBn(lock.amount), - })), - }; - }; - - // Handle payee callback. payee with `Account` type is returned as an key value pair, with all - // others strings. This function handles both cases and formats into a unified structure. - static handlePayeeCallback = (address: string, result: AnyApi): void => { - const payeeHuman = result.toHuman(); - let payeeFinal: PayeeConfig; - - if (payeeHuman !== null) { - if (typeof payeeHuman === 'string') { - const destination = payeeHuman as PayeeOptions; - payeeFinal = { - destination, - account: null, - }; - } else { - const payeeEntry = Object.entries(payeeHuman); - const destination = `${payeeEntry[0][0]}` as PayeeOptions; - const account = `${payeeEntry[0][1]}` as MaybeAddress; - payeeFinal = { - destination, - account, - }; - } - this.payees[address] = payeeFinal; - } - }; - - // Handle pool membership and claim commission callback. - static handlePoolMembershipCallback = async ( - api: ApiPromise, - address: string, - poolMembersResult: AnyApi, - claimPermissionsResult: AnyApi - ): Promise => { - // If pool membership is `null`, remove pool membership data from class data and exit early. - // This skips claim permission data as well as user would not have claim permissions if they are - // not in a pool. - const membership = poolMembersResult?.unwrapOr(undefined)?.toHuman(); - if (!membership) { - delete this.poolMemberships[address]; - return; - } - - // Format pool membership data. - const unlocking = Object.entries(membership?.unbondingEras || {}).map( - ([e, v]) => ({ - era: Number(rmCommas(e as string)), - value: new BigNumber(rmCommas(v as string)), - }) - ); - membership.points = rmCommas(membership?.points || '0'); - const balance = new BigNumber( - ( - await api.call.nominationPoolsApi.pointsToBalance( - membership.poolId, - membership.points - ) - )?.toString() || '0' - ); - const claimPermission = - claimPermissionsResult?.toString() || 'Permissioned'; - - // Persist formatted pool membership data to class. - this.poolMemberships[address] = { - ...membership, - address, - balance, - claimPermission, - unlocking, - }; - }; - - // Handle nominations callback. - static handleNominations = ( - address: string, - nominatorsResult: AnyApi - ): void => { - const nominators = nominatorsResult.unwrapOr(null); - - this.nominations[address] = - nominators === null - ? defaultNominations - : { - targets: nominators.targets.toHuman(), - submittedIn: nominators.submittedIn.toHuman(), - }; - }; - - // Gets an `ActiveBalance` from class members for the given address if it exists. - static getAccountBalances = (address: string): ActiveBalance | undefined => { - const ledger = this.ledgers[address]; - const balances = this.balances[address]; - const payee = this.payees[address]; - const poolMembership = this.poolMemberships[address]; - const nominations = this.nominations[address]; - - if (balances === undefined) { - // Account info has not synced yet. Note that `ledger` may not exist and therefore cannot be - // tested. + // Gets an `AccountBalances` subscription from class members for the given address if it exists. + static getAccountBalances = ( + network: NetworkName, + address: string + ): ActiveBalance | undefined => { + const accountBalances = SubscriptionsController.get( + network, + `accountBalances-${address}` + ) as AccountBalances; + + // Account info has not synced yet - exit early. + if (!accountBalances) { return undefined; } + const ledger = accountBalances.ledger; + const balances = accountBalances.balance; + const payee = accountBalances.payee; + const poolMembership = accountBalances.poolMembership; + const nominations = accountBalances.nominations; + return { ledger, balances, @@ -313,22 +93,6 @@ export class BalancesController { }; }; - // ------------------------------------------------------ - // Subscription handling. - // ------------------------------------------------------ - - // Unsubscribe from all subscriptions and reset class members. - static unsubscribe = (): void => { - Object.values(this.#unsubs).forEach((unsub) => { - unsub(); - }); - this.#unsubs = {}; - }; - - // ------------------------------------------------------ - // Class helpers. - // ------------------------------------------------------ - // Checks if event detailis a valid `new-account-balance` event. Note that `ledger` may not exist // and therefore cannot be tested. static isValidNewAccountBalanceEvent = ( diff --git a/packages/app/src/controllers/Subscriptions/types.ts b/packages/app/src/controllers/Subscriptions/types.ts index d835634a5..bff570d7e 100644 --- a/packages/app/src/controllers/Subscriptions/types.ts +++ b/packages/app/src/controllers/Subscriptions/types.ts @@ -1,6 +1,7 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import type { AccountBalances } from 'model/Subscribe/AccountBalances'; import type { ActiveEra } from 'model/Subscribe/ActiveEra'; import type { BlockNumber } from 'model/Subscribe/BlockNumber'; import type { Bonded } from 'model/Subscribe/Bonded'; @@ -12,6 +13,7 @@ import type { StakingMetrics } from 'model/Subscribe/StakingMetrics'; // Define all possible subscription classes. export type Subscription = + | AccountBalances | ActiveEra | BlockNumber | Bonded diff --git a/packages/app/src/hooks/useActiveBalances/index.tsx b/packages/app/src/hooks/useActiveBalances/index.tsx index 912dd890c..491bfe47c 100644 --- a/packages/app/src/hooks/useActiveBalances/index.tsx +++ b/packages/app/src/hooks/useActiveBalances/index.tsx @@ -170,7 +170,10 @@ export const useActiveBalances = ({ for (const account of uniqueAccounts) { // Adds an active balance record if it exists in `BalancesController`. if (account) { - const accountBalances = BalancesController.getAccountBalances(account); + const accountBalances = BalancesController.getAccountBalances( + network, + account + ); if (accountBalances) { newActiveBalances[account] = accountBalances; } diff --git a/packages/app/src/model/Subscribe/AccountBalances/index.ts b/packages/app/src/model/Subscribe/AccountBalances/index.ts new file mode 100644 index 000000000..d9ecc7684 --- /dev/null +++ b/packages/app/src/model/Subscribe/AccountBalances/index.ts @@ -0,0 +1,232 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import BigNumber from 'bignumber.js'; +import type { Balances, Ledger, Nominations } from 'contexts/Balances/types'; +import type { PoolMembership } from 'contexts/Pools/types'; +import type { PayeeConfig } from 'contexts/Setup/types'; +import { ApiController } from 'controllers/Api'; +import { BalancesController } from 'controllers/Balances'; +import { defaultNominations } from 'controllers/Balances/defaults'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import { stringToBn } from 'library/Utils'; +import type { PapiApi } from 'model/Api/types'; +import type { Subscription } from 'rxjs'; +import { combineLatest } from 'rxjs'; +import type { AnyApi, NetworkName } from 'types'; + +export class AccountBalances implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName; + + // Active subscription. + #sub: Subscription; + + // Account to subscribe to. + #address: string; + + // Account ledger. + ledger: Ledger | undefined; + + // Account balances. + balance: Balances; + + // Payee config. + payee: PayeeConfig | undefined; + + // Pool membership. + poolMembership: PoolMembership | undefined; + + // Account nominations. + nominations: Nominations; + + constructor(network: NetworkName, address: string) { + this.#network = network; + this.#address = address; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + const bestOrFinalized = 'best'; + + if (pApi && this.#sub === undefined) { + const sub = combineLatest([ + pApi.query.Staking.Ledger.watchValue(this.#address, bestOrFinalized), + pApi.query.System.Account.watchValue(this.#address, bestOrFinalized), + pApi.query.Balances.Locks.watchValue(this.#address, bestOrFinalized), + pApi.query.Staking.Payee.watchValue(this.#address, bestOrFinalized), + pApi.query.NominationPools.PoolMembers.watchValue( + this.#address, + bestOrFinalized + ), + pApi.query.NominationPools.ClaimPermissions.watchValue( + this.#address, + bestOrFinalized + ), + pApi.query.Staking.Nominators.watchValue( + this.#address, + bestOrFinalized + ), + ]).subscribe( + async ([ + ledger, + account, + locks, + payee, + poolMembers, + claimPermissions, + nominators, + ]) => { + this.handleLedger(ledger); + this.handleAccount(account, locks); + this.handlePayee(payee); + + await this.handlePoolMembership( + pApi, + poolMembers, + claimPermissions + ); + this.handleNominations(nominators); + + // Send updated account state back to UI. + const accountBalance = { + address: this.#address, + ledger: this.ledger, + balances: this.balance, + payee: this.payee, + poolMembership: this.poolMembership, + nominations: this.nominations, + }; + + document.dispatchEvent( + new CustomEvent('new-account-balance', { detail: accountBalance }) + ); + } + ); + + this.#sub = sub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Handle ledger result. + handleLedger = (ledger: AnyApi): void => { + // If ledger is null, remove from class. + if (!ledger) { + this.ledger = undefined; + } else { + const { stash, total, active, unlocking } = ledger; + + // Send stash address to UI as event if not presently imported. + if (!BalancesController.accounts.includes(stash.toString())) { + document.dispatchEvent( + new CustomEvent('new-external-account', { + detail: { address: stash.toString() }, + }) + ); + } + + this.ledger = { + stash: stash.toString(), + active: stringToBn(active.toString()), + total: stringToBn(total.toString()), + unlocking: unlocking.map( + ({ era, value }: { era: number; value: bigint }) => ({ + era: Number(era), + value: stringToBn(value.toString()), + }) + ), + }; + } + }; + + // Handle account callback. + handleAccount = ( + { data: accountData, nonce }: AnyApi, + locksResult: AnyApi + ): void => { + this.balance = { + nonce, + balance: { + free: stringToBn(accountData.free.toString()), + reserved: stringToBn(accountData.reserved.toString()), + frozen: stringToBn(accountData.frozen.toString()), + }, + locks: locksResult.map((lock: { id: AnyApi; amount: bigint }) => ({ + id: lock.id.asText().trim(), + amount: stringToBn(lock.amount.toString()), + })), + }; + }; + + // Handle payee callback. + handlePayee = (result: AnyApi): void => { + if (result === undefined) { + this.payee = undefined; + } else { + this.payee = { + destination: result.type || null, + account: result.value || undefined, + }; + } + }; + + // Handle pool membership and claim commission callback. + handlePoolMembership = async ( + pApi: PapiApi, + poolMembers: AnyApi, + claimPermissionResult: AnyApi + ): Promise => { + // If pool membership is `null`, remove pool membership data from class data and exit early. + // This skips claim permission data as well as user would not have claim permissions if they are + // not in a pool. + if (!poolMembers) { + this.poolMembership = undefined; + return; + } + + const unlocking = poolMembers?.unbonding_eras.map(([e, v]: AnyApi) => ({ + era: e, + value: new BigNumber((v as bigint).toString()), + })); + + const apiResult = await pApi.apis.NominationPoolsApi.points_to_balance( + poolMembers.pool_id, + poolMembers.points + ); + const balance = new BigNumber(apiResult?.toString() || 0); + const claimPermission = claimPermissionResult?.type || 'Permissioned'; + + this.poolMembership = { + address: this.#address, + poolId: poolMembers.pool_id, + points: poolMembers.points, + balance, + lastRecordedRewardCounter: poolMembers.last_recorded_reward_counter, + unbondingEras: unlocking, // NOTE: This is a duplicate of `unlocking`. + claimPermission, + unlocking, + }; + }; + + // Handle nominations callback. + handleNominations = (nominators: AnyApi): void => { + this.nominations = !nominators + ? defaultNominations + : { + targets: nominators.targets, + submittedIn: nominators.submitted_in, + }; + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} From 9af050a95d9696420a2b25f5749932dc508ac473 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 14:19:00 +0700 Subject: [PATCH 33/84] fetch one `BondedPool` --- .../src/contexts/Pools/BondedPools/index.tsx | 9 +-- .../src/model/Entries/BondedPools/index.tsx | 78 +++++++++++-------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/packages/app/src/contexts/Pools/BondedPools/index.tsx b/packages/app/src/contexts/Pools/BondedPools/index.tsx index 89ab2984d..2c57d526c 100644 --- a/packages/app/src/contexts/Pools/BondedPools/index.tsx +++ b/packages/app/src/contexts/Pools/BondedPools/index.tsx @@ -135,13 +135,8 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { // Queries a bonded pool and injects ID and addresses to a result. const queryBondedPool = async (id: number) => { - if (!api) { - return null; - } - - const bondedPool: AnyApi = ( - await api.query.nominationPools.bondedPools(id) - ).toHuman(); + const { pApi } = ApiController.get(network); + const bondedPool = new BondedPools(pApi).fetchOne(id); if (!bondedPool) { return null; diff --git a/packages/app/src/model/Entries/BondedPools/index.tsx b/packages/app/src/model/Entries/BondedPools/index.tsx index a6ae9466b..9fa767917 100644 --- a/packages/app/src/model/Entries/BondedPools/index.tsx +++ b/packages/app/src/model/Entries/BondedPools/index.tsx @@ -21,46 +21,60 @@ export class BondedPools { return this; } - format() { + async fetchOne(id: number) { + const result = + await this.#pApi.query.NominationPools.BondedPools.getValue(id); + + if (!result) { + return null; + } + return this.formatPool(result); + } + + format(entry?: AnyApi) { return Object.fromEntries( - this.bondedPools.map( + (entry ? [entry] : this.bondedPools).map( ({ keyArgs, value }: { keyArgs: [number]; value: AnyApi }) => { const id = keyArgs[0]; + const pool = this.formatPool(value); + return [id, pool]; + } + ) + ); + } - const maybeCommissionCurrent = value.commission.current; - const commissionCurrent = !maybeCommissionCurrent - ? null - : [ - perbillToPercent(maybeCommissionCurrent[0]).toString(), - maybeCommissionCurrent[1], - ]; + formatPool(value: AnyApi) { + const maybeCommissionCurrent = value.commission.current; + const commissionCurrent = !maybeCommissionCurrent + ? null + : [ + perbillToPercent(maybeCommissionCurrent[0]).toString(), + maybeCommissionCurrent[1], + ]; - const commissionMax = value.commission.max; - const commissionMaxPercent = !commissionMax - ? null - : perbillToPercent(new BigNumber(value.commission.max)); + const commissionMax = value.commission.max; + const commissionMaxPercent = !commissionMax + ? null + : perbillToPercent(new BigNumber(value.commission.max)); - const commissionChangeRate = value.commission.change_rate; + const commissionChangeRate = value.commission.change_rate; - const commission = { - current: commissionCurrent, - claimPermission: value.commission.claim_permission?.type || null, - max: commissionMaxPercent, - changeRate: commissionChangeRate || null, - throttleFrom: value.commission.throttle_from || null, - }; + const commission = { + current: commissionCurrent, + claimPermission: value.commission.claim_permission?.type || null, + max: commissionMaxPercent, + changeRate: commissionChangeRate || null, + throttleFrom: value.commission.throttle_from || null, + }; - const pool = { - commission, - points: value.points.toString(), - memberCounter: value.member_counter.toString(), - roles: value.roles, - state: value.state.type, - }; + const pool = { + commission, + points: value.points.toString(), + memberCounter: value.member_counter.toString(), + roles: value.roles, + state: value.state.type, + }; - return [id, pool]; - } - ) - ); + return pool; } } From 8a85908f338ce7e0f4cbac6087e18fc26ce78c01 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 14:28:40 +0700 Subject: [PATCH 34/84] stop fetching pool member entries --- .../src/canvas/CreatePool/Summary/index.tsx | 8 --- .../src/canvas/JoinPool/Overview/JoinForm.tsx | 8 --- .../app/src/canvas/PoolMembers/Members.tsx | 17 +----- .../contexts/Pools/PoolMembers/defaults.ts | 3 -- .../src/contexts/Pools/PoolMembers/index.tsx | 53 +------------------ .../src/contexts/Pools/PoolMembers/types.ts | 3 -- packages/app/src/library/Headers/Sync.tsx | 9 +--- .../src/pages/Pools/Home/PoolStats/index.tsx | 18 ++++--- 8 files changed, 15 insertions(+), 104 deletions(-) diff --git a/packages/app/src/canvas/CreatePool/Summary/index.tsx b/packages/app/src/canvas/CreatePool/Summary/index.tsx index d02c4c649..ff55af33c 100644 --- a/packages/app/src/canvas/CreatePool/Summary/index.tsx +++ b/packages/app/src/canvas/CreatePool/Summary/index.tsx @@ -7,7 +7,6 @@ import { unitToPlanck } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useSetup } from 'contexts/Setup'; import { Warning } from 'library/Form/Warning'; import { useBatchCall } from 'hooks/useBatchCall'; @@ -37,7 +36,6 @@ export const Summary = ({ section }: SetupStepProps) => { const { accountHasSigner } = useImportedAccounts(); const { getPoolSetup, removeSetupProgress } = useSetup(); const { activeAccount, activeProxy } = useActiveAccounts(); - const { queryPoolMember, addToPoolMembers } = usePoolMembers(); const { queryBondedPool, addToBondedPools } = useBondedPools(); const poolId = lastPoolId.plus(1); @@ -82,12 +80,6 @@ export const Summary = ({ section }: SetupStepProps) => { const pool = await queryBondedPool(poolId.toNumber()); addToBondedPools(pool); - // Query and add account to poolMembers list. - const member = await queryPoolMember(activeAccount); - if (member) { - addToPoolMembers(member); - } - // Reset setup progress. removeSetupProgress('pool', activeAccount); }, diff --git a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx index e9c2181d4..31024e3fd 100644 --- a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx +++ b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx @@ -17,7 +17,6 @@ import { useBatchCall } from 'hooks/useBatchCall'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; import { useSetup } from 'contexts/Setup'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { defaultPoolProgress } from 'contexts/Setup/defaults'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; import { SubmitTx } from 'library/SubmitTx'; @@ -42,7 +41,6 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { const { getSignerWarnings } = useSignerWarnings(); const { getTransferOptions } = useTransferOptions(); const largestTxFee = useBondGreatestFee({ bondFor: 'pool' }); - const { queryPoolMember, addToPoolMembers } = usePoolMembers(); const { pool: { totalPossibleBond }, @@ -112,12 +110,6 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { } }, callbackInBlock: async () => { - // Query and add account to poolMembers list - const member = await queryPoolMember(activeAccount); - if (member) { - addToPoolMembers(member); - } - // Reset local storage setup progress setActiveAccountSetup('pool', defaultPoolProgress); }, diff --git a/packages/app/src/canvas/PoolMembers/Members.tsx b/packages/app/src/canvas/PoolMembers/Members.tsx index 4eebd8b07..c207ae15c 100644 --- a/packages/app/src/canvas/PoolMembers/Members.tsx +++ b/packages/app/src/canvas/PoolMembers/Members.tsx @@ -4,20 +4,15 @@ import { faBars } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useTranslation } from 'react-i18next'; -import { usePlugins } from 'contexts/Plugins'; import { useActivePool } from 'contexts/Pools/ActivePool'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useTheme } from 'contexts/Themes'; import { CardWrapper } from 'library/Card/Wrappers'; import { useNetwork } from 'contexts/Network'; -import { MembersList as DefaultMemberList } from './Lists/Default'; import { MembersList as FetchPageMemberList } from './Lists/FetchPage'; export const Members = () => { const { t } = useTranslation('pages'); const { mode } = useTheme(); - const { pluginEnabled } = usePlugins(); - const { getMembersOfPoolFromNode } = usePoolMembers(); const { activePool, isOwner, isBouncer } = useActivePool(); const { colors } = useNetwork().networkData; @@ -78,17 +73,7 @@ export const Members = () => { )} - {pluginEnabled('subscan') ? ( - - ) : ( - - )} + ); diff --git a/packages/app/src/contexts/Pools/PoolMembers/defaults.ts b/packages/app/src/contexts/Pools/PoolMembers/defaults.ts index 5574bccb0..d27bff742 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/defaults.ts +++ b/packages/app/src/contexts/Pools/PoolMembers/defaults.ts @@ -7,12 +7,9 @@ import type { PoolMemberContext } from './types'; export const defaultPoolMembers: PoolMemberContext = { fetchPoolMembersMetaBatch: (k, v, r) => {}, queryPoolMember: (who) => new Promise((resolve) => resolve(null)), - getMembersOfPoolFromNode: (poolId) => null, - addToPoolMembers: (m) => {}, removePoolMember: (w) => {}, poolMembersApi: [], setPoolMembersApi: (p) => {}, - poolMembersNode: [], meta: {}, fetchedPoolMembersApi: 'unsynced', setFetchedPoolMembersApi: (s) => {}, diff --git a/packages/app/src/contexts/Pools/PoolMembers/index.tsx b/packages/app/src/contexts/Pools/PoolMembers/index.tsx index 8d2f28f36..80f7b9e2b 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/index.tsx +++ b/packages/app/src/contexts/Pools/PoolMembers/index.tsx @@ -25,9 +25,6 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { const { pluginEnabled } = usePlugins(); const { activeAccount } = useActiveAccounts(); - // Store pool members from node. - const [poolMembersNode, setPoolMembersNode] = useState([]); - // Store pool members from api. const [poolMembersApi, setPoolMembersApi] = useState([]); @@ -49,7 +46,6 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { // Clear existing state for network refresh useEffectIgnoreInitial(() => { - setPoolMembersNode([]); setPoolMembersApi([]); unsubscribeAndResetMeta(); }, [network]); @@ -62,12 +58,7 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { // Initial setup for fetching members if Subscan is not enabled. Ensure poolMembers are reset if // subscan is disabled. useEffectIgnoreInitial(() => { - if (!pluginEnabled('subscan')) { - if (isReady) { - fetchPoolMembersNode(); - } - } else { - setPoolMembersNode([]); + if (pluginEnabled('subscan')) { setPoolMembersApi([]); } return () => { @@ -77,7 +68,6 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { const unsubscribe = () => { unsubscribeAndResetMeta(); - setPoolMembersNode([]); setPoolMembersApi([]); }; @@ -88,26 +78,6 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { setStateWithRef({}, setPoolMembersMetaBatch, poolMembersMetaBatchesRef); }; - // Fetch all pool members entries from node. - const fetchPoolMembersNode = async () => { - if (!api) { - return; - } - const result = await api.query.nominationPools.poolMembers.entries(); - const newMembers = result.map(([keys, val]: AnyApi) => { - const who = keys.toHuman()[0]; - const { poolId } = val.toHuman(); - return { - who, - poolId, - }; - }); - setPoolMembersNode(newMembers); - }; - - const getMembersOfPoolFromNode = (poolId: number) => - poolMembersNode.filter((p) => String(p.poolId) === String(poolId)) ?? null; - // queries a pool member and formats to `PoolMember`. const queryPoolMember = async (who: MaybeAddress) => { if (!api) { @@ -221,26 +191,10 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { ); }; - // Removes a member from the member list and updates state. + // Removes a member from the member list and updates state. Requires subscan to be enabled. const removePoolMember = (who: MaybeAddress) => { - // If Subscan is enabled, update API state, otherwise, update node state. if (pluginEnabled('subscan')) { setPoolMembersApi(poolMembersApi.filter((p) => p.who !== who) ?? []); - } else { - setPoolMembersNode(poolMembersNode.filter((p) => p.who !== who) ?? []); - } - }; - - // Adds a record to poolMembers. - // Currently only used when an account joins or creates a pool. - const addToPoolMembers = (member: { who: string; poolId: number }) => { - if (!member || pluginEnabled('subscan')) { - return; - } - - const exists = poolMembersNode.find((m) => m.who === member.who); - if (!exists) { - setPoolMembersNode(poolMembersNode.concat(member)); } }; @@ -260,10 +214,7 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { value={{ fetchPoolMembersMetaBatch, queryPoolMember, - getMembersOfPoolFromNode, - addToPoolMembers, removePoolMember, - poolMembersNode, poolMembersApi, setPoolMembersApi, fetchedPoolMembersApi: fetchedPoolMembersApi.current, diff --git a/packages/app/src/contexts/Pools/PoolMembers/types.ts b/packages/app/src/contexts/Pools/PoolMembers/types.ts index e7b6dd30d..bed2f11b4 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/types.ts +++ b/packages/app/src/contexts/Pools/PoolMembers/types.ts @@ -7,10 +7,7 @@ import type { AnyMetaBatch, MaybeAddress } from 'types'; export interface PoolMemberContext { fetchPoolMembersMetaBatch: (k: string, v: AnyMetaBatch[], r: boolean) => void; queryPoolMember: (who: MaybeAddress) => Promise; - getMembersOfPoolFromNode: (poolId: number) => PoolMember[] | null; - addToPoolMembers: (member: PoolMember) => void; removePoolMember: (w: MaybeAddress) => void; - poolMembersNode: PoolMember[]; meta: AnyMetaBatch; poolMembersApi: PoolMember[]; setPoolMembersApi: (p: PoolMember[]) => void; diff --git a/packages/app/src/library/Headers/Sync.tsx b/packages/app/src/library/Headers/Sync.tsx index 8c9349688..e9d954c4e 100644 --- a/packages/app/src/library/Headers/Sync.tsx +++ b/packages/app/src/library/Headers/Sync.tsx @@ -3,9 +3,7 @@ import { pageFromUri } from '@w3ux/utils'; import { useLocation } from 'react-router-dom'; -import { usePlugins } from 'contexts/Plugins'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useValidators } from 'contexts/Validators/ValidatorEntries'; import { usePayouts } from 'contexts/Payouts'; import { Spinner } from './Spinner'; @@ -17,10 +15,8 @@ export const Sync = () => { const { pathname } = useLocation(); const { pendingNonces } = useTxMeta(); const { payoutsSynced } = usePayouts(); - const { pluginEnabled } = usePlugins(); const { validators } = useValidators(); const { bondedPools } = useBondedPools(); - const { poolMembersNode } = usePoolMembers(); // Keep syncing if on nominate page and still fetching payouts. const onNominateSyncing = () => { @@ -37,10 +33,7 @@ export const Sync = () => { // member sync if Subscan is enabled. const onPoolsSyncing = () => { if (pageFromUri(pathname, 'overview') === 'pools') { - if ( - !bondedPools.length || - (!poolMembersNode.length && !pluginEnabled('subscan')) - ) { + if (!bondedPools.length) { return true; } } diff --git a/packages/app/src/pages/Pools/Home/PoolStats/index.tsx b/packages/app/src/pages/Pools/Home/PoolStats/index.tsx index 3f0bf279f..6215f6b9a 100644 --- a/packages/app/src/pages/Pools/Home/PoolStats/index.tsx +++ b/packages/app/src/pages/Pools/Home/PoolStats/index.tsx @@ -14,6 +14,7 @@ import type { PoolStatLabel } from 'library/Announcements/types'; import { useOverlay } from 'kits/Overlay/Provider'; import { Wrapper } from 'library/Announcements/Wrappers'; import { planckToUnitBn } from 'library/Utils'; +import { usePlugins } from 'contexts/Plugins'; export const PoolStats = () => { const { t } = useTranslation('pages'); @@ -21,6 +22,7 @@ export const PoolStats = () => { const { networkData: { units, unit }, } = useNetwork(); + const { pluginEnabled } = usePlugins(); const { activePool } = useActivePool(); const { getCurrentCommission } = usePoolCommission(); @@ -67,13 +69,15 @@ export const PoolStats = () => { { label: t('pools.poolMembers'), value: `${memberCounter}`, - button: { - text: t('pools.browseMembers'), - onClick: () => { - openCanvas({ key: 'PoolMembers', size: 'xl' }); - }, - disabled: memberCounter === '0', - }, + button: pluginEnabled('subscan') + ? { + text: t('pools.browseMembers'), + onClick: () => { + openCanvas({ key: 'PoolMembers', size: 'xl' }); + }, + disabled: memberCounter === '0', + } + : undefined, }, { label: t('pools.totalBonded'), From b4805db66ec845a6d3bfe7014777bd208171dbcc Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 14:42:14 +0700 Subject: [PATCH 35/84] discontinue `queryPoolMember` --- .../contexts/Pools/PoolMembers/defaults.ts | 1 - .../src/contexts/Pools/PoolMembers/index.tsx | 21 ------------------- .../src/contexts/Pools/PoolMembers/types.ts | 1 - 3 files changed, 23 deletions(-) diff --git a/packages/app/src/contexts/Pools/PoolMembers/defaults.ts b/packages/app/src/contexts/Pools/PoolMembers/defaults.ts index d27bff742..39a1d3a0f 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/defaults.ts +++ b/packages/app/src/contexts/Pools/PoolMembers/defaults.ts @@ -6,7 +6,6 @@ import type { PoolMemberContext } from './types'; export const defaultPoolMembers: PoolMemberContext = { fetchPoolMembersMetaBatch: (k, v, r) => {}, - queryPoolMember: (who) => new Promise((resolve) => resolve(null)), removePoolMember: (w) => {}, poolMembersApi: [], setPoolMembersApi: (p) => {}, diff --git a/packages/app/src/contexts/Pools/PoolMembers/index.tsx b/packages/app/src/contexts/Pools/PoolMembers/index.tsx index 80f7b9e2b..02cf7dcdd 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/index.tsx +++ b/packages/app/src/contexts/Pools/PoolMembers/index.tsx @@ -78,26 +78,6 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { setStateWithRef({}, setPoolMembersMetaBatch, poolMembersMetaBatchesRef); }; - // queries a pool member and formats to `PoolMember`. - const queryPoolMember = async (who: MaybeAddress) => { - if (!api) { - return null; - } - - const poolMember: AnyApi = ( - await api.query.nominationPools.poolMembers(who) - ).toHuman(); - - if (!poolMember) { - return null; - } - - return { - who, - poolId: poolMember.poolId, - } as PoolMember; - }; - /* Fetches a new batch of pool member metadata. structure: @@ -213,7 +193,6 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { void; - queryPoolMember: (who: MaybeAddress) => Promise; removePoolMember: (w: MaybeAddress) => void; meta: AnyMetaBatch; poolMembersApi: PoolMember[]; From 2dbca3428a289c21069c34c54f85f45f38766b00 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 14:53:55 +0700 Subject: [PATCH 36/84] fix --- packages/app/src/model/Subscribe/AccountBalances/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/app/src/model/Subscribe/AccountBalances/index.ts b/packages/app/src/model/Subscribe/AccountBalances/index.ts index d9ecc7684..612124f6a 100644 --- a/packages/app/src/model/Subscribe/AccountBalances/index.ts +++ b/packages/app/src/model/Subscribe/AccountBalances/index.ts @@ -204,9 +204,10 @@ export class AccountBalances implements Unsubscribable { this.poolMembership = { address: this.#address, poolId: poolMembers.pool_id, - points: poolMembers.points, + points: poolMembers.points.toString(), balance, - lastRecordedRewardCounter: poolMembers.last_recorded_reward_counter, + lastRecordedRewardCounter: + poolMembers.last_recorded_reward_counter.toString(), unbondingEras: unlocking, // NOTE: This is a duplicate of `unlocking`. claimPermission, unlocking, From 2af98fbda9eca0bb31235bb6760d5b874f178b0c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 14:54:09 +0700 Subject: [PATCH 37/84] remove discontinued list --- .../src/canvas/PoolMembers/Lists/Default.tsx | 104 ------------------ .../app/src/canvas/PoolMembers/Lists/types.ts | 6 - 2 files changed, 110 deletions(-) delete mode 100644 packages/app/src/canvas/PoolMembers/Lists/Default.tsx diff --git a/packages/app/src/canvas/PoolMembers/Lists/Default.tsx b/packages/app/src/canvas/PoolMembers/Lists/Default.tsx deleted file mode 100644 index 32e1937c6..000000000 --- a/packages/app/src/canvas/PoolMembers/Lists/Default.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { poolMembersPerPage } from 'library/List/defaults'; -import { useApi } from 'contexts/Api'; -import { usePoolMembers } from 'contexts/Pools/PoolMembers'; -import { List, ListStatusHeader, Wrapper as ListWrapper } from 'library/List'; -import { Pagination } from 'library/List/Pagination'; -import { ListProvider } from 'library/List/context'; -import type { Sync } from '@w3ux/types'; -import { Member } from './Member'; -import type { DefaultMembersListProps } from './types'; -import type { PoolMember } from 'contexts/Pools/PoolMembers/types'; -import { MotionContainer } from 'library/List/MotionContainer'; - -export const MembersListInner = ({ - pagination, - batchKey, - members: initialMembers, -}: DefaultMembersListProps) => { - const { t } = useTranslation('pages'); - const { isReady, activeEra } = useApi(); - const { fetchPoolMembersMetaBatch } = usePoolMembers(); - - // current page - const [page, setPage] = useState(1); - - // default list of validators - const [membersDefault, setMembersDefault] = - useState(initialMembers); - - // manipulated list (ordering, filtering) of payouts - const [members, setMembers] = useState(initialMembers); - - // is this the initial fetch - const [fetched, setFetched] = useState('unsynced'); - - // pagination - const totalPages = Math.ceil(members.length / poolMembersPerPage); - const pageEnd = page * poolMembersPerPage - 1; - const pageStart = pageEnd - (poolMembersPerPage - 1); - - // get throttled subset or entire list - const listMembers = members.slice(pageStart).slice(0, poolMembersPerPage); - - // handle validator list bootstrapping - const setupMembersList = () => { - setMembersDefault(initialMembers); - setMembers(initialMembers); - fetchPoolMembersMetaBatch(batchKey, initialMembers, false); - setFetched('synced'); - }; - - // Refetch list when list changes. - useEffect(() => { - if (initialMembers !== membersDefault) { - setFetched('unsynced'); - } - }, [initialMembers]); - - // Configure list when network is ready to fetch. - useEffect(() => { - if (isReady && !activeEra.index.isZero() && fetched === 'unsynced') { - setupMembersList(); - } - }, [isReady, fetched, activeEra.index]); - - return !members.length ? null : ( - - - {listMembers.length > 0 && pagination && ( - - )} - {fetched !== 'synced' ? ( - - {t('pools.fetchingMemberList')}... - - ) : ( - - {listMembers.map((member: PoolMember, index: number) => ( - - ))} - - )} - - - ); -}; - -export const MembersList = (props: DefaultMembersListProps) => { - const { selectToggleable } = props; - return ( - - - - ); -}; diff --git a/packages/app/src/canvas/PoolMembers/Lists/types.ts b/packages/app/src/canvas/PoolMembers/Lists/types.ts index 0e66b3a75..9103cd8c3 100644 --- a/packages/app/src/canvas/PoolMembers/Lists/types.ts +++ b/packages/app/src/canvas/PoolMembers/Lists/types.ts @@ -1,18 +1,12 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { AnyJson } from '@w3ux/types'; - export interface MembersListProps { pagination: boolean; batchKey: string; selectToggleable?: boolean; } -export type DefaultMembersListProps = MembersListProps & { - members: AnyJson; -}; - export type FetchpageMembersListProps = MembersListProps & { memberCount: string; }; From 0b83be34b0ccb4a3a0ec68c8097eb047ecaf5b51 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 16:07:34 +0700 Subject: [PATCH 38/84] migrate Pool multi queries to classes --- .../src/contexts/Pools/BondedPools/index.tsx | 52 ++++++++----------- .../src/model/Query/NominatorsMulti/index.ts | 39 ++++++++++++++ .../model/Query/PoolMetadataMulti/index.ts | 28 ++++++++++ 3 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 packages/app/src/model/Query/NominatorsMulti/index.ts create mode 100644 packages/app/src/model/Query/PoolMetadataMulti/index.ts diff --git a/packages/app/src/contexts/Pools/BondedPools/index.tsx b/packages/app/src/contexts/Pools/BondedPools/index.tsx index 2c57d526c..12d2b283f 100644 --- a/packages/app/src/contexts/Pools/BondedPools/index.tsx +++ b/packages/app/src/contexts/Pools/BondedPools/index.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import { u8aUnwrapBytes, u8aToString } from '@polkadot/util'; -import { rmCommas, setStateWithRef, shuffle } from '@w3ux/utils'; +import { setStateWithRef, shuffle } from '@w3ux/utils'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; import type { @@ -25,6 +25,8 @@ import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; import { SyncController } from 'controllers/Sync'; import { BondedPools } from 'model/Entries/BondedPools'; import { ApiController } from 'controllers/Api'; +import { NominatorsMulti } from 'model/Query/NominatorsMulti'; +import { PoolMetadataMulti } from 'model/Query/PoolMetadataMulti'; export const BondedPoolsContext = createContext( defaultBondedPoolsContext @@ -35,7 +37,6 @@ export const useBondedPools = () => useContext(BondedPoolsContext); export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { - api, isReady, activeEra, poolsConfig: { lastPoolId }, @@ -67,32 +68,30 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { const fetchBondedPools = async () => { const { pApi } = ApiController.get(network); - if (!api || !pApi || bondedPoolsSynced.current !== 'unsynced') { + if (!pApi || bondedPoolsSynced.current !== 'unsynced') { return; } bondedPoolsSynced.current = 'syncing'; - const ids: number[] = []; - // Get and format bonded pool entries. + const ids: number[] = []; + const idsMulti: [number][] = []; const bondedPoolsEntries = (await new BondedPools(pApi).fetch()).format(); - let exposures = Object.entries(bondedPoolsEntries).map( - ([id, pool]: AnyApi) => { + const exposures = shuffle( + Object.entries(bondedPoolsEntries).map(([id, pool]: AnyApi) => { ids.push(id); + idsMulti.push([id]); return getPoolWithAddresses(id, pool); - } + }) ); - exposures = shuffle(exposures); setStateWithRef(exposures, setBondedPools, bondedPoolsRef); // Fetch pools metadata. - const metadataMulti = await api.query.nominationPools.metadata.multi(ids); + const metadataQuery = await new PoolMetadataMulti(pApi, idsMulti).fetch(); setPoolsMetadata( - Object.fromEntries( - metadataMulti.map((m, i) => [ids[i], String(m.toHuman())]) - ) + Object.fromEntries(metadataQuery.map((m, i) => [ids[i], m])) ); bondedPoolsSynced.current = 'synced'; @@ -101,35 +100,28 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { // Fetches pool nominations and updates state. const fetchPoolsNominations = async () => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return; } const ids: number[] = []; - const nominationsMulti = await api.query.staking.nominators.multi( - bondedPools.map(({ addresses, id }) => { - ids.push(id); - return addresses.stash; - }) - ); + const stashes: [string][] = bondedPools.map(({ addresses, id }) => { + ids.push(id); + return [addresses.stash]; + }); + const nominationsMulti = await new NominatorsMulti(pApi, stashes).fetch(); setPoolsNominations(formatPoolsNominations(nominationsMulti, ids)); }; // Format raw pool nominations data. const formatPoolsNominations = (raw: AnyJson, ids: number[]) => Object.fromEntries( - raw.map((n: AnyJson, i: number) => { - const human = n.toHuman() as PoolNominations; - if (!human) { + raw.map((nominator: AnyJson, i: number) => { + if (!nominator) { return [ids[i], null]; } - return [ - ids[i], - { - ...human, - submittedIn: rmCommas(human.submittedIn), - }, - ]; + return [ids[i], { ...nominator }]; }) ); diff --git a/packages/app/src/model/Query/NominatorsMulti/index.ts b/packages/app/src/model/Query/NominatorsMulti/index.ts new file mode 100644 index 000000000..c9279c596 --- /dev/null +++ b/packages/app/src/model/Query/NominatorsMulti/index.ts @@ -0,0 +1,39 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class NominatorsMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + let result; + try { + result = await this.#pApi.query.Staking.Nominators.getValues( + this.#addresses + ); + + return result.map((nominator) => { + if (!nominator) { + return undefined; + } + return { + submittedIn: String(nominator.submitted_in), + suppressed: nominator.suppressed, + targets: nominator.targets, + }; + }); + } catch (e) { + // Silently fail. + } + + return null; + } +} diff --git a/packages/app/src/model/Query/PoolMetadataMulti/index.ts b/packages/app/src/model/Query/PoolMetadataMulti/index.ts new file mode 100644 index 000000000..e28220f5b --- /dev/null +++ b/packages/app/src/model/Query/PoolMetadataMulti/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class PoolMetadataMulti { + #pApi: PapiApi; + + #ids: [number][]; + + constructor(pApi: PapiApi, ids: [number][]) { + this.#pApi = pApi; + this.#ids = ids; + } + + async fetch() { + try { + const result = await this.#pApi.query.NominationPools.Metadata.getValues( + this.#ids + ); + return result.map((metadata) => metadata.asText()); + } catch (e) { + // Silently fail. + } + + return []; + } +} From 61c0fe6fc45db7de5e4e77d971537f492b195053 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 16:28:38 +0700 Subject: [PATCH 39/84] account proxies to `AccountProxies` subscription class --- packages/app/src/contexts/Proxies/index.tsx | 109 ++++++++---------- .../src/controllers/Subscriptions/types.ts | 2 + .../model/Subscribe/AccountProxies/index.ts | 55 +++++++++ .../model/Subscribe/AccountProxies/types.ts | 19 +++ packages/app/src/types.ts | 2 + 5 files changed, 129 insertions(+), 58 deletions(-) create mode 100644 packages/app/src/model/Subscribe/AccountProxies/index.ts create mode 100644 packages/app/src/model/Subscribe/AccountProxies/types.ts diff --git a/packages/app/src/contexts/Proxies/index.tsx b/packages/app/src/contexts/Proxies/index.tsx index 1ef1c9b0c..21c5fa3aa 100644 --- a/packages/app/src/contexts/Proxies/index.tsx +++ b/packages/app/src/contexts/Proxies/index.tsx @@ -1,14 +1,12 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import { addedTo, ellipsisFn, localStorageOrDefault, matchedProperties, removedFrom, - rmCommas, setStateWithRef, } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; @@ -33,6 +31,10 @@ import type { ProxyDelegate, } from './types'; import { defaultNetwork } from 'contexts/Network/defaults'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { AccountProxies } from 'model/Subscribe/AccountProxies'; +import { useEventListener } from 'usehooks-ts'; +import { isCustomEvent } from 'controllers/utils'; export const ProxiesContext = createContext( defaults.defaultProxiesContext @@ -51,7 +53,6 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { // Store the proxy accounts of each imported account. const [proxies, setProxies] = useState([]); const proxiesRef = useRef(proxies); - const unsubs = useRef>({}); // Store the last network proxies were synced on. const [lastSyncedNetwork, setLastSyncedNetwork] = @@ -106,17 +107,11 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { addOrReplaceOtherAccount(importResult.account, importResult.type); } } else { - const unsub = unsubs.current[address]; - if (unsub) { - unsub(); - } + SubscriptionsController.remove(network, `accountProxies-${address}`); } }); - - unsubs.current = Object.fromEntries( - Object.entries(unsubs.current).filter(([key]) => !removed.includes(key)) - ); }; + // Sync added accounts. const handleAddedAccounts = () => { addedTo(accounts, proxies, ['address'])?.map(({ address }) => @@ -147,45 +142,11 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { if (!api) { return undefined; } - - const unsub = await api.queryMulti( - [[api.query.proxy.proxies, address]], - async ([result]) => { - const data = result.toHuman(); - const newProxies = data[0]; - const reserved = new BigNumber(rmCommas(data[1])); - - if (newProxies.length) { - setStateWithRef( - [...proxiesRef.current] - .filter(({ delegator }) => delegator !== address) - .concat({ - address, - delegator: address, - delegates: newProxies.map((d: AnyApi) => ({ - delegate: d.delegate.toString(), - proxyType: d.proxyType.toString(), - })), - reserved, - }), - setProxies, - proxiesRef - ); - } else { - // no proxies: remove stale proxies if already in list. - setStateWithRef( - [...proxiesRef.current].filter( - ({ delegator }) => delegator !== address - ), - setProxies, - proxiesRef - ); - } - } + SubscriptionsController.set( + network, + `accountProxies-${address}`, + new AccountProxies(network, address) ); - - unsubs.current[address] = unsub; - return unsub; }; // Gets the delegates of the given account. @@ -215,6 +176,7 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { return []; } + // TODO: Migrate to query class. const result: AnyApi = (await api.query.proxy.proxies(delegator)).toHuman(); let addDelegatorAsExternal = false; @@ -246,6 +208,43 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { .find((p) => p.delegator === delegator) ?.delegates.find((d) => d.delegate === delegate) ?? null; + // Handle account proxies events. + const handleAccountProxies = (e: Event) => { + if (isCustomEvent(e)) { + const { address: eventAddress, proxies: eventProxies } = e.detail; + + const newProxies = eventProxies[0]; + const reserved = new BigNumber(eventProxies[1].toString()); + + if (newProxies.length) { + setStateWithRef( + [...proxiesRef.current] + .filter(({ delegator }) => delegator !== eventAddress) + .concat({ + address: eventAddress, + delegator: eventAddress, + delegates: newProxies.map((d: AnyApi) => ({ + delegate: d.delegate.toString(), + proxyType: d.proxy_type.type.toString(), + })), + reserved, + }), + setProxies, + proxiesRef + ); + } else { + // no proxies: remove stale proxies if already in list. + setStateWithRef( + [...proxiesRef.current].filter( + ({ delegator }) => delegator !== eventAddress + ), + setProxies, + proxiesRef + ); + } + } + }; + // If active proxy has not yet been set, check local storage `activeProxy` & set it as active // proxy if it is the delegate of `activeAccount`. useEffectIgnoreInitial(() => { @@ -293,20 +292,14 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { } }, [accounts, isReady]); - // Reset active proxy state, unsubscribe from subscriptions on network change & unmount. + // Reset active proxy state on network change & unmount. useEffectIgnoreInitial(() => { setStateWithRef([], setProxies, proxiesRef); setActiveProxy(null, false); - unsubAll(); - return () => unsubAll(); }, [network]); - const unsubAll = () => { - for (const unsub of Object.values(unsubs.current)) { - unsub(); - } - unsubs.current = {}; - }; + const documentRef = useRef(document); + useEventListener('new-account-proxies', handleAccountProxies, documentRef); return ( => { + try { + const { pApi } = ApiController.get(this.#network); + + if (pApi && this.#sub === undefined) { + const unsub = pApi.query.Proxy.Proxies.watchValue( + this.#address + ).subscribe((proxies) => { + document.dispatchEvent( + new CustomEvent('new-account-proxies', { + detail: { address: this.#address, proxies }, + }) + ); + }); + this.#sub = unsub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/AccountProxies/types.ts b/packages/app/src/model/Subscribe/AccountProxies/types.ts new file mode 100644 index 000000000..3e2f79155 --- /dev/null +++ b/packages/app/src/model/Subscribe/AccountProxies/types.ts @@ -0,0 +1,19 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export type AccountProxy = [ + { + delay: number; + delegate: string; + proxy_type: { + type: string; + value: undefined; + }; + }[], + bigint, +]; + +export interface AccountProxiesEvent { + address: string; + proxies: AccountProxy; +} diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index 1651a9343..22ae59ee6 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -23,6 +23,7 @@ import type { BlockNumberEventDetail } from 'model/Subscribe/BlockNumber/types'; import type { BondedAccount } from 'contexts/Bonded/types'; import type { FastUnstakeConfigResult } from 'model/Subscribe/FastUnstakeConfig/types'; import type { FastUnstakeQueueResult } from 'contexts/FastUnstake/types'; +import type { AccountProxiesEvent } from 'model/Subscribe/AccountProxies/types'; declare global { interface Window { @@ -47,6 +48,7 @@ declare global { 'new-active-pool': CustomEvent; 'new-fast-unstake-config': CustomEvent; 'new-fast-unstake-deposit': CustomEvent; + 'new-account-proxies': CustomEvent; 'new-bonded-account': CustomEvent; 'new-sync-status': CustomEvent; 'new-external-account': CustomEvent<{ address: string }>; From 9f1d0a1cc77a7be3e8054fe6a54f58cd4823c780 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 16:35:49 +0700 Subject: [PATCH 40/84] migrate proxy query to `ProxiesQuery` --- packages/app/src/contexts/Proxies/index.tsx | 16 +++++------ .../app/src/model/Query/ProxiesQuery/index.ts | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 packages/app/src/model/Query/ProxiesQuery/index.ts diff --git a/packages/app/src/contexts/Proxies/index.tsx b/packages/app/src/contexts/Proxies/index.tsx index 21c5fa3aa..865955765 100644 --- a/packages/app/src/contexts/Proxies/index.tsx +++ b/packages/app/src/contexts/Proxies/index.tsx @@ -35,6 +35,8 @@ import { SubscriptionsController } from 'controllers/Subscriptions'; import { AccountProxies } from 'model/Subscribe/AccountProxies'; import { useEventListener } from 'usehooks-ts'; import { isCustomEvent } from 'controllers/utils'; +import { ProxiesQuery } from 'model/Query/ProxiesQuery'; +import { ApiController } from 'controllers/Api'; export const ProxiesContext = createContext( defaults.defaultProxiesContext @@ -43,8 +45,8 @@ export const ProxiesContext = createContext( export const useProxies = () => useContext(ProxiesContext); export const ProxiesProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); - const { api, isReady } = useApi(); const { accounts } = useImportedAccounts(); const { addExternalAccount } = useExternalAccounts(); const { addOrReplaceOtherAccount } = useOtherAccounts(); @@ -139,9 +141,6 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { }; const subscribeToProxies = async (address: string) => { - if (!api) { - return undefined; - } SubscriptionsController.set( network, `accountProxies-${address}`, @@ -172,15 +171,16 @@ export const ProxiesProvider = ({ children }: { children: ReactNode }) => { // Queries the chain to check if the given delegator & delegate pair is valid proxy. Used when a // proxy account is being manually declared. const handleDeclareDelegate = async (delegator: string) => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return []; } - // TODO: Migrate to query class. - const result: AnyApi = (await api.query.proxy.proxies(delegator)).toHuman(); + const result = await new ProxiesQuery(pApi, delegator).fetch(); + const proxy = result[0] || []; let addDelegatorAsExternal = false; - for (const { delegate: newDelegate } of result[0] || []) { + for (const { delegate: newDelegate } of proxy) { if ( accounts.find(({ address }) => address === newDelegate) && !delegates[newDelegate] diff --git a/packages/app/src/model/Query/ProxiesQuery/index.ts b/packages/app/src/model/Query/ProxiesQuery/index.ts new file mode 100644 index 000000000..49866d57b --- /dev/null +++ b/packages/app/src/model/Query/ProxiesQuery/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ProxiesQuery { + #pApi: PapiApi; + + #address: string; + + constructor(pApi: PapiApi, address: string) { + this.#pApi = pApi; + this.#address = address; + } + + async fetch() { + try { + const result = await this.#pApi.query.Proxy.Proxies.getValue( + this.#address + ); + return result; + } catch (e) { + // Subscription failed. + } + + return undefined; + } +} From 6cb0cd8799d1c127d7162b72fbdb4ab76df8f2bc Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 16:43:39 +0700 Subject: [PATCH 41/84] add `IdentityOfMulti` class --- .../src/model/Query/IdentityOfMulti/index.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 packages/app/src/model/Query/IdentityOfMulti/index.ts diff --git a/packages/app/src/model/Query/IdentityOfMulti/index.ts b/packages/app/src/model/Query/IdentityOfMulti/index.ts new file mode 100644 index 000000000..d2c8dc5c5 --- /dev/null +++ b/packages/app/src/model/Query/IdentityOfMulti/index.ts @@ -0,0 +1,29 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +// TODO: Hook up and test. +export class IdentityOfMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + try { + const result = await this.#pApi.query.Identity.IdentityOf.getValues( + this.#addresses + ); + return result; + } catch (e) { + // Silently fail. + } + + return null; + } +} From c8bc325133f1b710e098b1d9db5d69d922f6a19c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 17:39:14 +0700 Subject: [PATCH 42/84] use `bestOrFinalized` = `best` --- .../model/Subscribe/AccountProxies/index.ts | 4 +- .../src/model/Subscribe/ActiveEra/index.ts | 65 ++++++++++--------- .../src/model/Subscribe/BlockNumber/index.ts | 5 +- .../app/src/model/Subscribe/Bonded/index.tsx | 4 +- .../Subscribe/FastUnstakeConfig/index.ts | 6 +- .../model/Subscribe/FastUnstakeQueue/index.ts | 4 +- .../model/Subscribe/NetworkMetrics/index.ts | 15 +++-- .../src/model/Subscribe/PoolsConfig/index.ts | 31 ++++++--- .../model/Subscribe/StakingMetrics/index.ts | 20 +++--- 9 files changed, 93 insertions(+), 61 deletions(-) diff --git a/packages/app/src/model/Subscribe/AccountProxies/index.ts b/packages/app/src/model/Subscribe/AccountProxies/index.ts index 8c07e8f04..b2688cace 100644 --- a/packages/app/src/model/Subscribe/AccountProxies/index.ts +++ b/packages/app/src/model/Subscribe/AccountProxies/index.ts @@ -30,8 +30,10 @@ export class AccountProxies implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; const unsub = pApi.query.Proxy.Proxies.watchValue( - this.#address + this.#address, + bestOrFinalized ).subscribe((proxies) => { document.dispatchEvent( new CustomEvent('new-account-proxies', { diff --git a/packages/app/src/model/Subscribe/ActiveEra/index.ts b/packages/app/src/model/Subscribe/ActiveEra/index.ts index 9df6b011a..935fa28f2 100644 --- a/packages/app/src/model/Subscribe/ActiveEra/index.ts +++ b/packages/app/src/model/Subscribe/ActiveEra/index.ts @@ -32,43 +32,44 @@ export class ActiveEra implements Unsubscribable { if (pApi && this.#sub === undefined) { // Testing the active era subscription. - const sub = pApi.query.Staking.ActiveEra.watchValue().subscribe( - (activeEra) => { - // Store active era. - this.activeEra = { - index: new BigNumber(activeEra.index.toString()), - start: new BigNumber(activeEra.start.toString()), - }; + const bestOrFinalized = 'best'; + const sub = pApi.query.Staking.ActiveEra.watchValue( + bestOrFinalized + ).subscribe((activeEra) => { + // Store active era. + this.activeEra = { + index: new BigNumber(activeEra.index.toString()), + start: new BigNumber(activeEra.start.toString()), + }; - // Unsubscribe to staking metrics if it exists. - const subStakingMetrics = SubscriptionsController.get( - this.#network, - 'stakingMetrics' - ); + // Unsubscribe to staking metrics if it exists. + const subStakingMetrics = SubscriptionsController.get( + this.#network, + 'stakingMetrics' + ); - if (subStakingMetrics) { - subStakingMetrics.subscribe(); - SubscriptionsController.remove(this.#network, 'stakingMetrics'); - } + if (subStakingMetrics) { + subStakingMetrics.subscribe(); + SubscriptionsController.remove(this.#network, 'stakingMetrics'); + } - // Subscribe to staking metrics with new active era. - SubscriptionsController.set( + // Subscribe to staking metrics with new active era. + SubscriptionsController.set( + this.#network, + 'stakingMetrics', + new StakingMetrics( this.#network, - 'stakingMetrics', - new StakingMetrics( - this.#network, - this.activeEra, - BigNumber.max(0, this.activeEra.index.minus(1)) - ) - ); + this.activeEra, + BigNumber.max(0, this.activeEra.index.minus(1)) + ) + ); - document.dispatchEvent( - new CustomEvent('new-active-era', { - detail: { activeEra }, - }) - ); - } - ); + document.dispatchEvent( + new CustomEvent('new-active-era', { + detail: { activeEra }, + }) + ); + }); this.#sub = sub; } } catch (e) { diff --git a/packages/app/src/model/Subscribe/BlockNumber/index.ts b/packages/app/src/model/Subscribe/BlockNumber/index.ts index 43b93fbe0..13d48f8e9 100644 --- a/packages/app/src/model/Subscribe/BlockNumber/index.ts +++ b/packages/app/src/model/Subscribe/BlockNumber/index.ts @@ -26,7 +26,10 @@ export class BlockNumber implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { - const unsub = pApi.query.System.Number.watchValue().subscribe((num) => { + const bestOrFinalized = 'best'; + const unsub = pApi.query.System.Number.watchValue( + bestOrFinalized + ).subscribe((num) => { // Update class block number. this.blockNumber = num.toString(); diff --git a/packages/app/src/model/Subscribe/Bonded/index.tsx b/packages/app/src/model/Subscribe/Bonded/index.tsx index 7e96e7780..25281319f 100644 --- a/packages/app/src/model/Subscribe/Bonded/index.tsx +++ b/packages/app/src/model/Subscribe/Bonded/index.tsx @@ -31,8 +31,10 @@ export class Bonded implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; const unsub = pApi.query.Staking.Bonded.watchValue( - this.#address + this.#address, + bestOrFinalized ).subscribe((controller) => { const account: BondedAccount = { address: this.#address, diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts index 94237a35f..7248c5963 100644 --- a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts @@ -27,9 +27,11 @@ export class FastUnstakeConfig implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + const sub = combineLatest([ - pApi.query.FastUnstake.Head.watchValue(), - pApi.query.FastUnstake.CounterForQueue.watchValue(), + pApi.query.FastUnstake.Head.watchValue(bestOrFinalized), + pApi.query.FastUnstake.CounterForQueue.watchValue(bestOrFinalized), ]).subscribe(([head, counterForQueue]) => { const config: FastUnstakeConfigResult = { head, diff --git a/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts index 156b4e2a4..bb99f7509 100644 --- a/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts +++ b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts @@ -30,8 +30,10 @@ export class FastUnstakeQueue implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; const unsub = pApi.query.FastUnstake.Queue.watchValue( - this.#address + this.#address, + bestOrFinalized ).subscribe((queue) => { this.queue = queue; diff --git a/packages/app/src/model/Subscribe/NetworkMetrics/index.ts b/packages/app/src/model/Subscribe/NetworkMetrics/index.ts index 66728a7ff..20834c460 100644 --- a/packages/app/src/model/Subscribe/NetworkMetrics/index.ts +++ b/packages/app/src/model/Subscribe/NetworkMetrics/index.ts @@ -25,12 +25,17 @@ export class NetworkMetrics implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; const sub = combineLatest([ - pApi.query.Balances.TotalIssuance.watchValue(), - pApi.query.Auctions.AuctionCounter.watchValue(), - pApi.query.ParaSessionInfo.EarliestStoredSession.watchValue(), - pApi.query.FastUnstake.ErasToCheckPerBlock.watchValue(), - pApi.query.Staking.MinimumActiveStake.watchValue(), + pApi.query.Balances.TotalIssuance.watchValue(bestOrFinalized), + pApi.query.Auctions.AuctionCounter.watchValue(bestOrFinalized), + pApi.query.ParaSessionInfo.EarliestStoredSession.watchValue( + bestOrFinalized + ), + pApi.query.FastUnstake.ErasToCheckPerBlock.watchValue( + bestOrFinalized + ), + pApi.query.Staking.MinimumActiveStake.watchValue(bestOrFinalized), ]).subscribe( ([ totalIssuance, diff --git a/packages/app/src/model/Subscribe/PoolsConfig/index.ts b/packages/app/src/model/Subscribe/PoolsConfig/index.ts index 4977602e7..a15885ac3 100644 --- a/packages/app/src/model/Subscribe/PoolsConfig/index.ts +++ b/packages/app/src/model/Subscribe/PoolsConfig/index.ts @@ -28,17 +28,28 @@ export class PoolsConfig implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; const sub = combineLatest([ - pApi.query.NominationPools.CounterForPoolMembers.watchValue(), - pApi.query.NominationPools.CounterForBondedPools.watchValue(), - pApi.query.NominationPools.CounterForRewardPools.watchValue(), - pApi.query.NominationPools.LastPoolId.watchValue(), - pApi.query.NominationPools.MaxPoolMembers.watchValue(), - pApi.query.NominationPools.MaxPoolMembersPerPool.watchValue(), - pApi.query.NominationPools.MaxPools.watchValue(), - pApi.query.NominationPools.MinCreateBond.watchValue(), - pApi.query.NominationPools.MinJoinBond.watchValue(), - pApi.query.NominationPools.GlobalMaxCommission.watchValue(), + pApi.query.NominationPools.CounterForPoolMembers.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.CounterForBondedPools.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.CounterForRewardPools.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.LastPoolId.watchValue(bestOrFinalized), + pApi.query.NominationPools.MaxPoolMembers.watchValue(bestOrFinalized), + pApi.query.NominationPools.MaxPoolMembersPerPool.watchValue( + bestOrFinalized + ), + pApi.query.NominationPools.MaxPools.watchValue(bestOrFinalized), + pApi.query.NominationPools.MinCreateBond.watchValue(bestOrFinalized), + pApi.query.NominationPools.MinJoinBond.watchValue(bestOrFinalized), + pApi.query.NominationPools.GlobalMaxCommission.watchValue( + bestOrFinalized + ), ]).subscribe( ([ counterForPoolMembers, diff --git a/packages/app/src/model/Subscribe/StakingMetrics/index.ts b/packages/app/src/model/Subscribe/StakingMetrics/index.ts index 221eac282..638ac9bbb 100644 --- a/packages/app/src/model/Subscribe/StakingMetrics/index.ts +++ b/packages/app/src/model/Subscribe/StakingMetrics/index.ts @@ -39,21 +39,25 @@ export class StakingMetrics implements Unsubscribable { const { pApi } = ApiController.get(this.#network); if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; const sub = combineLatest([ - pApi.query.Staking.CounterForValidators.watchValue(), - pApi.query.Staking.MaxValidatorsCount.watchValue(), - pApi.query.Staking.ValidatorCount.watchValue(), + pApi.query.Staking.CounterForValidators.watchValue(bestOrFinalized), + pApi.query.Staking.MaxValidatorsCount.watchValue(bestOrFinalized), + pApi.query.Staking.ValidatorCount.watchValue(bestOrFinalized), pApi.query.Staking.ErasValidatorReward.watchValue( - this.#previousEra.toString() + this.#previousEra.toString(), + bestOrFinalized ), pApi.query.Staking.ErasTotalStake.watchValue( - this.#previousEra.toString() + this.#previousEra.toString(), + bestOrFinalized ), - pApi.query.Staking.MinNominatorBond.watchValue(), + pApi.query.Staking.MinNominatorBond.watchValue(bestOrFinalized), pApi.query.Staking.ErasTotalStake.watchValue( - this.#activeEra.index.toString() + this.#activeEra.index.toString(), + bestOrFinalized ), - pApi.query.Staking.CounterForNominators.watchValue(), + pApi.query.Staking.CounterForNominators.watchValue(bestOrFinalized), ]).subscribe( ([ counterForValidators, From c24eb3e3bc3861d5050ea0e2e0802cbc0c32aa5e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 17:43:51 +0700 Subject: [PATCH 43/84] prepare `ActivePoolAccount` class --- packages/app/src/contexts/Balances/index.tsx | 1 + .../src/contexts/Pools/ActivePool/index.tsx | 1 + .../src/controllers/Subscriptions/index.ts | 2 +- .../src/controllers/Subscriptions/types.ts | 2 + .../Subscribe/ActivePoolAccount/index.ts | 66 +++++++++++++++++++ 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/model/Subscribe/ActivePoolAccount/index.ts diff --git a/packages/app/src/contexts/Balances/index.tsx b/packages/app/src/contexts/Balances/index.tsx index ab8087b0a..8b103f502 100644 --- a/packages/app/src/contexts/Balances/index.tsx +++ b/packages/app/src/contexts/Balances/index.tsx @@ -71,6 +71,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { }); if (peopleApi) { ActivePoolsController.syncPools( + network, api, peopleApi, peopleApiStatus, diff --git a/packages/app/src/contexts/Pools/ActivePool/index.tsx b/packages/app/src/contexts/Pools/ActivePool/index.tsx index 0669e0b67..c858403dd 100644 --- a/packages/app/src/contexts/Pools/ActivePool/index.tsx +++ b/packages/app/src/contexts/Pools/ActivePool/index.tsx @@ -101,6 +101,7 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { SyncController.dispatch('active-pools', 'syncing'); if (peopleApi) { ActivePoolsController.syncPools( + network, api, peopleApi, peopleApiStatus, diff --git a/packages/app/src/controllers/Subscriptions/index.ts b/packages/app/src/controllers/Subscriptions/index.ts index c71fa6552..b4f2cbdcb 100644 --- a/packages/app/src/controllers/Subscriptions/index.ts +++ b/packages/app/src/controllers/Subscriptions/index.ts @@ -45,7 +45,7 @@ export class SubscriptionsController { // Sets a new subscription for a network. static set( - network: NetworkName, + network: NetworkName | SystemChainId, subscriptionId: string, subscription: Subscription ): void { diff --git a/packages/app/src/controllers/Subscriptions/types.ts b/packages/app/src/controllers/Subscriptions/types.ts index ded1ee291..41a612d6a 100644 --- a/packages/app/src/controllers/Subscriptions/types.ts +++ b/packages/app/src/controllers/Subscriptions/types.ts @@ -4,6 +4,7 @@ import type { AccountBalances } from 'model/Subscribe/AccountBalances'; import type { AccountProxies } from 'model/Subscribe/AccountProxies'; import type { ActiveEra } from 'model/Subscribe/ActiveEra'; +import type { ActivePoolAccount } from 'model/Subscribe/ActivePoolAccount'; import type { BlockNumber } from 'model/Subscribe/BlockNumber'; import type { Bonded } from 'model/Subscribe/Bonded'; import type { FastUnstakeConfig } from 'model/Subscribe/FastUnstakeConfig'; @@ -17,6 +18,7 @@ export type Subscription = | AccountBalances | AccountProxies | ActiveEra + | ActivePoolAccount | BlockNumber | Bonded | FastUnstakeConfig diff --git a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts new file mode 100644 index 000000000..25be11a0a --- /dev/null +++ b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts @@ -0,0 +1,66 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { ActivePoolItem } from 'controllers/ActivePools/types'; +import { ApiController } from 'controllers/Api'; +import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import { combineLatest, type Subscription } from 'rxjs'; +import type { NetworkName, SystemChainId } from 'types'; + +export class ActivePoolAccount implements Unsubscribable { + // The associated network for this instance. + #network: NetworkName | SystemChainId; + + // Active subscription. + #sub: Subscription; + + // Active pool item + #pool: ActivePoolItem; + + constructor(network: NetworkName | SystemChainId, pool: ActivePoolItem) { + this.#network = network; + this.#pool = pool; + this.subscribe(); + } + + subscribe = async (): Promise => { + try { + const { pApi } = ApiController.get(this.#network); + const bestOrFinalized = 'best'; + + const sub = combineLatest([ + pApi.query.NominationPools.BondedPools.watchValue( + this.#pool.id, + bestOrFinalized + ), + pApi.query.NominationPools.RewardPools.watchValue( + this.#pool.id, + bestOrFinalized + ), + pApi.query.System.Account.watchValue( + this.#pool.addresses.reward, + bestOrFinalized + ), + pApi.query.Staking.Nominators.watchValue( + this.#pool.addresses.stash, + bestOrFinalized + ), + ]).subscribe(async ([bondedPool, rewardPool, account, nominators]) => { + console.debug(bondedPool, rewardPool, account, nominators); + + // TODO: Implement callback. + }); + + this.#sub = sub; + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} From 9a5632ffbbd0d6e76d7d7dc909528c79998d7325 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 18:57:22 +0700 Subject: [PATCH 44/84] remove "all roles" from Pool UI --- packages/app/src/pages/Pools/Home/index.tsx | 31 --------------------- 1 file changed, 31 deletions(-) diff --git a/packages/app/src/pages/Pools/Home/index.tsx b/packages/app/src/pages/Pools/Home/index.tsx index 3b6bb253c..1771da4c1 100644 --- a/packages/app/src/pages/Pools/Home/index.tsx +++ b/packages/app/src/pages/Pools/Home/index.tsx @@ -9,8 +9,6 @@ import { CardWrapper } from 'library/Card/Wrappers'; import { PoolList } from 'library/PoolList'; import { StatBoxList } from 'library/StatBoxList'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; -import { useOverlay } from 'kits/Overlay/Provider'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { PoolListProvider } from 'library/PoolList/context'; import { Roles } from '../Roles'; import { ClosurePrompts } from './ClosurePrompts'; @@ -23,34 +21,17 @@ import { MinCreateBondStat } from './Stats/MinCreateBond'; import { MinJoinBondStat } from './Stats/MinJoinBond'; import { Status } from './Status'; import { PoolsTabsProvider, usePoolsTabs } from './context'; -import { useActivePools } from 'hooks/useActivePools'; -import { useBalances } from 'contexts/Balances'; import { PageTitle, PageRow, RowSection } from 'ui-structure'; import { WithdrawPrompt } from 'library/WithdrawPrompt'; -import { useSyncing } from 'hooks/useSyncing'; import { useNetwork } from 'contexts/Network'; export const HomeInner = () => { const { t } = useTranslation('pages'); const { network } = useNetwork(); const { favorites } = useFavoritePools(); - const { openModal } = useOverlay().modal; const { bondedPools } = useBondedPools(); - const { getPoolMembership } = useBalances(); - const { poolMembersipSyncing } = useSyncing(); - const { activeAccount } = useActiveAccounts(); const { activeTab, setActiveTab } = usePoolsTabs(); const { getPoolRoles, activePool } = useActivePool(); - const membership = getPoolMembership(activeAccount); - - const { activePools } = useActivePools({ - who: activeAccount, - }); - - // Calculate the number of _other_ pools the user has a role in. - const poolRoleCount = Object.keys(activePools).filter( - (poolId) => poolId !== String(membership?.poolId) - ).length; const ROW_HEIGHT = 220; @@ -81,18 +62,6 @@ export const HomeInner = () => { badge: String(favorites.length), }, ]} - button={ - !poolMembersipSyncing() && poolRoleCount > 0 - ? { - title: t('pools.allRoles'), - onClick: () => - openModal({ - key: 'AccountPoolRoles', - options: { who: activeAccount, activePools }, - }), - } - : undefined - } /> {activeTab === 0 && ( <> From 8e73d757bf5d242ede369ee285a9cf4e20aa88c3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 18:57:59 +0700 Subject: [PATCH 45/84] rm locales --- packages/app/src/locale/cn/pages.json | 1 - packages/app/src/locale/en/pages.json | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/app/src/locale/cn/pages.json b/packages/app/src/locale/cn/pages.json index 6e0901e6b..569edeb87 100644 --- a/packages/app/src/locale/cn/pages.json +++ b/packages/app/src/locale/cn/pages.json @@ -138,7 +138,6 @@ "addressCopied": "地址已复制到剪贴板", "addressInvalid": "地址无效", "allPools": "所有提名池", - "allRoles": "所有角色", "assigned": "己分配", "assignedToAnyAccount": " 您的主理人提名人守护人角色可以分配给任何帐户。", "availableToClaim": "成员可申领的奖励{{unit}}金额", diff --git a/packages/app/src/locale/en/pages.json b/packages/app/src/locale/en/pages.json index dd80be38d..650175747 100644 --- a/packages/app/src/locale/en/pages.json +++ b/packages/app/src/locale/en/pages.json @@ -140,7 +140,6 @@ "addressCopied": "Address Copied to Clipboard", "addressInvalid": "Address Invalid", "allPools": "All Pools", - "allRoles": "All Roles", "assigned": "Assigned", "assignedToAnyAccount": " Your Root, Nominator and Bouncer roles can be assigned to any account.", "availableToClaim": "The outstanding amount of {{unit}} available to claim by pool members.", From 29527860e1c40eb3fb8fb63518820cf0625ac967 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 20:19:03 +0700 Subject: [PATCH 46/84] init `ActivePoolAccount` subscription & remove multi role logic --- packages/app/src/contexts/Balances/index.tsx | 11 +- .../src/contexts/Pools/ActivePool/index.tsx | 71 ++-- .../contexts/Pools/BondedPools/defaults.ts | 1 - .../src/contexts/Pools/BondedPools/index.tsx | 64 ---- .../src/contexts/Pools/BondedPools/types.ts | 1 - packages/app/src/contexts/Staking/index.tsx | 5 + .../app/src/controllers/ActivePools/index.ts | 324 +++++------------- .../app/src/hooks/useActivePools/index.tsx | 13 +- .../Subscribe/ActivePoolAccount/index.ts | 131 ++++++- 9 files changed, 241 insertions(+), 380 deletions(-) diff --git a/packages/app/src/contexts/Balances/index.tsx b/packages/app/src/contexts/Balances/index.tsx index 8b103f502..7564d4fea 100644 --- a/packages/app/src/contexts/Balances/index.tsx +++ b/packages/app/src/contexts/Balances/index.tsx @@ -27,10 +27,10 @@ export const useBalances = () => useContext(BalancesContext); export const BalancesProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); + const { api, peopleApi } = useApi(); const { getBondedAccount } = useBonded(); const { accounts } = useImportedAccounts(); const createPoolAccounts = useCreatePoolAccounts(); - const { api, peopleApi, peopleApiStatus } = useApi(); const { activeAccount, activeProxy } = useActiveAccounts(); const controller = getBondedAccount(activeAccount); @@ -70,14 +70,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { addresses: { ...createPoolAccounts(Number(poolId)) }, }); if (peopleApi) { - ActivePoolsController.syncPools( - network, - api, - peopleApi, - peopleApiStatus, - address, - newPools - ); + ActivePoolsController.syncPools(network, address, newPools); } } } diff --git a/packages/app/src/contexts/Pools/ActivePool/index.tsx b/packages/app/src/contexts/Pools/ActivePool/index.tsx index c858403dd..ae5f69890 100644 --- a/packages/app/src/contexts/Pools/ActivePool/index.tsx +++ b/packages/app/src/contexts/Pools/ActivePool/index.tsx @@ -3,12 +3,11 @@ import { setStateWithRef } from '@w3ux/utils'; import type { ReactNode } from 'react'; -import { createContext, useContext, useMemo, useRef, useState } from 'react'; +import { createContext, useContext, useRef, useState } from 'react'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useApi } from '../../Api'; -import { useBondedPools } from '../BondedPools'; import type { ActivePoolContextState } from './types'; import { useCreatePoolAccounts } from 'hooks/useCreatePoolAccounts'; import { useBalances } from 'contexts/Balances'; @@ -27,30 +26,15 @@ export const useActivePool = () => useContext(ActivePoolContext); export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { getPoolMembership } = useBalances(); + const { isReady, api, peopleApi } = useApi(); const { activeAccount } = useActiveAccounts(); const createPoolAccounts = useCreatePoolAccounts(); - const { isReady, api, peopleApi, peopleApiStatus } = useApi(); - const { getAccountPoolRoles, bondedPools } = useBondedPools(); const membership = getPoolMembership(activeAccount); // Determine active pools to subscribe to. Dependencies of `activeAccount`, and `membership` mean // that this object is only recalculated when these values change. - const accountPoolIds = useMemo(() => { - const rollPoolIds: string[] = Object.keys( - getAccountPoolRoles(activeAccount) || {} - ); - - // If a membership subscription has resulted in an update that is inconsistent with - // `bondedPools`, add that role to the list of the account's pool roles. - if ( - membership?.poolId && - !rollPoolIds.includes(String(membership.poolId)) - ) { - rollPoolIds.push(String(membership.poolId)); - } - return rollPoolIds; - }, [activeAccount, bondedPools, membership]); + const accountPoolId = membership?.poolId ? String(membership.poolId) : null; // Store the currently selected active pool for the UI. Should default to the membership pool if // present. Used in event callback, therefore needs an accompanying ref. @@ -64,27 +48,21 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Only listen to the active account's active pools, otherwise return an empty array. NOTE: // `activePoolsRef` is needed to check if the pool has changed after the async call of fetching // pending rewards. - const { getActivePools, activePoolsRef, getPoolNominations } = useActivePools( - { - who: activeAccount, - onCallback: async () => { - // Sync: active pools synced once all account pools have been reported. - if ( - accountPoolIds.length <= - ActivePoolsController.getPools(activeAccount).length - ) { - SyncController.dispatch('active-pools', 'complete'); - } - }, - } - ); + const { getActivePool, activePoolsRef, getPoolNominations } = useActivePools({ + who: activeAccount, + onCallback: async () => { + if (ActivePoolsController.getPool(network, activeAccount)) { + SyncController.dispatch('active-pools', 'complete'); + } + }, + }); // Store the currently active pool's pending rewards for the active account. const [pendingPoolRewards, setPendingPoolRewards] = useState( new BigNumber(0) ); - const activePool = activePoolId ? getActivePools(activePoolId) : null; + const activePool = activePoolId ? getActivePool(activePoolId) : null; const activePoolNominations = activePoolId ? getPoolNominations(activePoolId) @@ -92,22 +70,17 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Sync active pool subscriptions. const syncActivePoolSubscriptions = async () => { - if (api && accountPoolIds.length) { - const newActivePools = accountPoolIds.map((pool) => ({ - id: pool, - addresses: { ...createPoolAccounts(Number(pool)) }, - })); + if (api && accountPoolId) { + const newActivePool = [ + { + id: accountPoolId, + addresses: { ...createPoolAccounts(Number(accountPoolId)) }, + }, + ]; SyncController.dispatch('active-pools', 'syncing'); if (peopleApi) { - ActivePoolsController.syncPools( - network, - api, - peopleApi, - peopleApiStatus, - activeAccount, - newActivePools - ); + ActivePoolsController.syncPools(network, activeAccount, newActivePool); } } else { // No active pools to sync. Mark as complete. @@ -119,7 +92,7 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { const assignActivePoolId = () => { // Membership takes priority, followed by the first pool the account has a role in. Falls back // to `null` if no active roles are found. - const initialActivePoolId = membership?.poolId || accountPoolIds[0] || null; + const initialActivePoolId = membership?.poolId || null; if (initialActivePoolId && !activePool) { setActivePoolId(String(initialActivePoolId)); } @@ -235,7 +208,7 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { syncActivePoolSubscriptions(); assignActivePoolId(); } - }, [network, isReady, accountPoolIds]); + }, [network, isReady, membership]); // Reset on network change and component unmount. NOTE: ActivePoolsController also unsubscribes on // network change; this is handled by the Api instance. diff --git a/packages/app/src/contexts/Pools/BondedPools/defaults.ts b/packages/app/src/contexts/Pools/BondedPools/defaults.ts index 0363dc217..5430aebb6 100644 --- a/packages/app/src/contexts/Pools/BondedPools/defaults.ts +++ b/packages/app/src/contexts/Pools/BondedPools/defaults.ts @@ -12,7 +12,6 @@ export const defaultBondedPoolsContext: BondedPoolsContextState = { removeFromBondedPools: (poolId) => {}, getPoolNominationStatus: (nominator, address) => {}, getPoolNominationStatusCode: (statuses) => '', - getAccountPoolRoles: (address) => null, replacePoolRoles: (poolId, roleEdits) => {}, poolSearchFilter: (filteredPools, searchTerm) => [], bondedPools: [], diff --git a/packages/app/src/contexts/Pools/BondedPools/index.tsx b/packages/app/src/contexts/Pools/BondedPools/index.tsx index 12d2b283f..71c72e9b9 100644 --- a/packages/app/src/contexts/Pools/BondedPools/index.tsx +++ b/packages/app/src/contexts/Pools/BondedPools/index.tsx @@ -6,7 +6,6 @@ import { setStateWithRef, shuffle } from '@w3ux/utils'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; import type { - AccountPoolRoles, BondedPool, BondedPoolsContextState, MaybePool, @@ -284,68 +283,6 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { } }; - // Gets all pools that the account has a role in. Returns an object with each pool role as keys, - // and array of pool ids as their values. - const accumulateAccountPoolRoles = (who: MaybeAddress): AccountPoolRoles => { - if (!who) { - return { - root: [], - depositor: [], - nominator: [], - bouncer: [], - }; - } - - const depositor = bondedPoolsRef.current - .filter((b) => b.roles.depositor === who) - .map((b) => b.id); - - const root = bondedPoolsRef.current - .filter((b: BondedPool) => b.roles.root === who) - .map((b) => b.id); - - const nominator = bondedPoolsRef.current - .filter((b) => b.roles.nominator === who) - .map((b) => b.id); - - const bouncer = bondedPoolsRef.current - .filter((b) => b.roles.bouncer === who) - .map((b) => b.id); - - const result = { - root, - depositor, - nominator, - bouncer, - }; - - return result; - }; - - // Gets a list of roles for all the pools the provided account has one or more roles in. - const getAccountPoolRoles = (who: MaybeAddress) => { - const allAccountRoles = accumulateAccountPoolRoles(who); - - // Reformat all roles object, keyed by pool id. - const pools: Record = {}; - - if (allAccountRoles) { - Object.entries(allAccountRoles).forEach(([role, poolIds]) => { - poolIds.forEach((poolId) => { - const exists = Object.keys(pools).find( - (k) => String(k) === String(poolId) - ); - if (!exists) { - pools[poolId] = [role]; - } else { - pools[poolId].push(role); - } - }); - }); - } - return pools; - }; - // Determine roles to replace from roleEdits const toReplace = (roleEdits: AnyJson) => { const root = roleEdits?.root?.newAddress ?? ''; @@ -415,7 +352,6 @@ export const BondedPoolsProvider = ({ children }: { children: ReactNode }) => { removeFromBondedPools, getPoolNominationStatus, getPoolNominationStatusCode, - getAccountPoolRoles, replacePoolRoles, poolSearchFilter, bondedPools, diff --git a/packages/app/src/contexts/Pools/BondedPools/types.ts b/packages/app/src/contexts/Pools/BondedPools/types.ts index 87af9c7af..62b669b3b 100644 --- a/packages/app/src/contexts/Pools/BondedPools/types.ts +++ b/packages/app/src/contexts/Pools/BondedPools/types.ts @@ -18,7 +18,6 @@ export interface BondedPoolsContextState { address: MaybeAddress ) => AnyApi; getPoolNominationStatusCode: (statuses: NominationStatuses | null) => string; - getAccountPoolRoles: (address: MaybeAddress) => AnyApi; replacePoolRoles: (poolId: number, roleEdits: AnyJson) => void; poolSearchFilter: (filteredPools: AnyFilter, searchTerm: string) => AnyJson[]; bondedPools: BondedPool[]; diff --git a/packages/app/src/contexts/Staking/index.tsx b/packages/app/src/contexts/Staking/index.tsx index f6f682619..1a84bd4c7 100644 --- a/packages/app/src/contexts/Staking/index.tsx +++ b/packages/app/src/contexts/Staking/index.tsx @@ -249,6 +249,11 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // NOTE: Only one page is fetched for each validator for now. const page = pages[0]; + // NOTE: Some pages turn up as undefined - might be worth exploring further. + if (!page) { + continue; + } + const { keyArgs, value: { others }, diff --git a/packages/app/src/controllers/ActivePools/index.ts b/packages/app/src/controllers/ActivePools/index.ts index 7ff42f010..c501c76ba 100644 --- a/packages/app/src/controllers/ActivePools/index.ts +++ b/packages/app/src/controllers/ActivePools/index.ts @@ -1,49 +1,28 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; -import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; -import type { ActivePool, PoolRoles } from 'contexts/Pools/ActivePool/types'; -import { IdentitiesController } from 'controllers/Identities'; -import type { AnyApi, MaybeAddress } from 'types'; +import type { MaybeAddress, NetworkName, SystemChainId } from 'types'; import type { AccountActivePools, AccountPoolNominations, - AccountUnsubs, ActivePoolItem, DetailActivePool, } from './types'; import { SyncController } from 'controllers/Sync'; -import type { Nominations } from 'contexts/Balances/types'; -import type { ApiPromise } from '@polkadot/api'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { ActivePoolAccount } from 'model/Subscribe/ActivePoolAccount'; +import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; export class ActivePoolsController { - // ------------------------------------------------------ - // Class members. - // ------------------------------------------------------ - // Pool ids that are being subscribed to. Keyed by address. static pools: Record = {}; - // Active pools that are being returned from subscriptions, keyed by account address, then pool - // id. - static activePools: Record = {}; - - // Active pool nominations, keyed by account address, then pool id. - static poolNominations: Record = {}; - - // Unsubscribe objects, keyed by account address, then pool id. - static #unsubs: Record = {}; - - // ------------------------------------------------------ - // Pool membership syncing. - // ------------------------------------------------------ + // Map from an address to its associated pool ids + static addressToPool: Record = {}; - // Subscribes to pools and unsubscribes from removed pools. + // Subscribes to new pools and unsubscribes from removed pools. static syncPools = async ( - api: ApiPromise, - peopleApi: ApiPromise, - peopleApiStatus: string, + network: NetworkName, address: MaybeAddress, newPools: ActivePoolItem[] ): Promise => { @@ -52,260 +31,125 @@ export class ActivePoolsController { } // Handle pools that have been removed. - this.handleRemovedPools(address, newPools); + this.handleRemovedPools(network, address); - const currentPools = this.getPools(address); + const currentPool = this.addressToPool[address]; // Determine new pools that need to be subscribed to. - const poolsAdded = newPools.filter( - (newPool) => !currentPools.find(({ id }) => id === newPool.id) - ); + const updatedPool = newPools.find((newPool) => currentPool === newPool.id) + ? false + : newPools[0]; - if (poolsAdded.length) { - // Subscribe to and add new pool data. - poolsAdded.forEach(async (pool) => { - this.pools[address] = currentPools.concat(pool); + if (updatedPool) { + this.pools[address] = newPools; - const unsub = await api.queryMulti( - [ - [api.query.nominationPools.bondedPools, pool.id], - [api.query.nominationPools.rewardPools, pool.id], - [api.query.system.account, pool.addresses.reward], - [api.query.staking.nominators, pool.addresses.stash], - ], - async ([ - bondedPool, - rewardPool, - accountData, - nominators, - ]): Promise => { - // NOTE: async: fetches identity data for roles. - await this.handleActivePoolCallback( - peopleApi, - peopleApiStatus, - address, - pool, - bondedPool, - rewardPool, - accountData - ); - this.handleNominatorsCallback(address, pool, nominators); + // Subscribe to and add new pool data. + SubscriptionsController.set( + network, + `activePool-${address}-${updatedPool.id}`, + new ActivePoolAccount(network, address, updatedPool) + ); - if ( - this.activePools?.[address]?.[pool.id] && - this.poolNominations?.[address]?.[pool.id] - ) { - document.dispatchEvent( - new CustomEvent('new-active-pool', { - detail: { - address, - pool: this.activePools[address][pool.id], - nominations: this.poolNominations[address][pool.id], - }, - }) - ); - } - } - ); - this.setUnsub(address, pool.id, unsub); - }); + // Add pool id to address mapping. + this.addressToPool[address] = updatedPool.id; } else { // Status: Pools Synced Completed. SyncController.dispatch('active-pools', 'complete'); } }; - // Handle active pool callback. - static handleActivePoolCallback = async ( - peopleApi: ApiPromise, - peopleApiStatus: string, - address: string, - pool: ActivePoolItem, - bondedPoolResult: AnyApi, - rewardPoolResult: AnyApi, - accountDataResult: AnyApi - ): Promise => { - const bondedPool = bondedPoolResult?.unwrapOr(undefined)?.toHuman(); - const rewardPool = rewardPoolResult?.unwrapOr(undefined)?.toHuman(); - const balance = accountDataResult.data; - const rewardAccountBalance = balance?.free.toString(); - - if (peopleApi && peopleApiStatus === 'ready') { - // Fetch identities for roles and expand `bondedPool` state to store them. - bondedPool.roleIdentities = await IdentitiesController.fetch( - peopleApi, - this.getUniqueRoleAddresses(bondedPool.roles) - ); - } - - // Only persist the active pool to class state (and therefore dispatch an event) if both the - // bonded pool and reward pool are returned. - if (bondedPool && rewardPool) { - const newPool = { - id: Number(pool.id), - addresses: pool.addresses, - bondedPool, - rewardPool, - rewardAccountBalance, - }; - - this.setActivePool(address, pool.id, newPool); - } else { - // Invalid pools were returned. To signal pool was synced, set active pool to `null`. - this.setActivePool(address, pool.id, null); - } - }; - - // Handle nominators callback. - static handleNominatorsCallback = ( - address: string, - pool: ActivePoolItem, - nominatorsResult: AnyApi - ): void => { - const maybeNewNominations = nominatorsResult.unwrapOr(null); - - const newNominations: Nominations = - maybeNewNominations === null - ? defaultPoolNominations - : { - targets: maybeNewNominations.targets.toHuman(), - submittedIn: maybeNewNominations.submittedIn.toHuman(), - }; - - this.setPoolNominations(address, pool.id, newNominations); - }; - // Remove pools that no longer exist. static handleRemovedPools = ( - address: string, - newPools: ActivePoolItem[] + network: NetworkName | SystemChainId, + address: string ): void => { - const currentPools = this.getPools(address); - - // Determine removed pools - current ones that no longer exist in `newPools`. - const poolsRemoved = currentPools.filter( - (pool) => !newPools.find((newPool) => newPool.id === pool.id) - ); - - // Unsubscribe from removed pool subscriptions. - poolsRemoved.forEach((pool) => { - if (this.#unsubs?.[address]?.[pool.id]) { - this.#unsubs[address][pool.id](); - } - delete this.activePools[address][pool.id]; - delete this.poolNominations[address][pool.id]; - }); + const currentPool = this.addressToPool[address]; - // Remove removed pools from class. - this.pools[address] = currentPools.filter( - (pool) => !poolsRemoved.includes(pool) - ); + if (currentPool) { + // Unsubscribe from removed pool subscription. + SubscriptionsController.remove( + network, + `activePool-${address}-${currentPool}` + ); - // Tidy up empty class state. - if (!this.pools[address].length) { + // Remove pool from class. + delete this.addressToPool[address]; delete this.pools[address]; } - - if (!this.activePools[address]) { - delete this.activePools[address]; - } - - if (!this.poolNominations[address]) { - delete this.poolNominations[address]; - } - if (!this.#unsubs[address]) { - delete this.#unsubs[address]; - } - }; - - // ------------------------------------------------------ - // Subscription handling. - // ------------------------------------------------------ - - // Unsubscribe from all subscriptions and reset class members. - static unsubscribe = (): void => { - Object.values(this.#unsubs).forEach((accountUnsubs) => { - Object.values(accountUnsubs).forEach((unsub) => { - unsub(); - }); - }); - - this.#unsubs = {}; }; // ------------------------------------------------------ // Getters. // ------------------------------------------------------ - // Gets pools for a provided address. - static getPools = (address: MaybeAddress): ActivePoolItem[] => { + // Gets pool for a provided address. + static getPool = ( + network: NetworkName, + address: MaybeAddress + ): ActivePoolItem | undefined => { if (!address) { - return []; + return undefined; } - return this.pools?.[address] || []; + const activePoolAccount = SubscriptionsController.get( + network, + `activePool-${address}-${this.addressToPool[address]}` + ) as ActivePoolAccount; + + return activePoolAccount?.pool || undefined; }; - // Gets active pools for a provided address. - static getActivePools = (address: MaybeAddress): AccountActivePools => { + // Gets active pool for a provided address. + static getActivePool = ( + network: NetworkName, + address: MaybeAddress + ): AccountActivePools => { if (!address) { return {}; } - return this.activePools?.[address] || {}; + const poolId = this.addressToPool[address]; + const activePool = SubscriptionsController.get( + network, + `activePool-${address}-${poolId}` + ) as ActivePoolAccount; + + if (!activePool) { + return {}; + } + + return { [poolId]: activePool?.activePool || null }; }; // Gets active pool nominations for a provided address. static getPoolNominations = ( + network: NetworkName, address: MaybeAddress ): AccountPoolNominations => { if (!address) { return {}; } - return this.poolNominations?.[address] || {}; - }; - - // Gets unique role addresses from a bonded pool's `roles` record. - static getUniqueRoleAddresses = (roles: PoolRoles): string[] => { - const roleAddresses: string[] = [ - ...new Set(Object.values(roles).filter((role) => role !== undefined)), - ]; - return roleAddresses; + const poolId = this.addressToPool[address]; + const activePool = SubscriptionsController.get( + network, + `activePool-${address}-${poolId}` + ) as ActivePoolAccount; + + return { + poolId: activePool?.poolNominations || defaultPoolNominations, + }; }; - // ------------------------------------------------------ - // Setters. - // ------------------------------------------------------ - - // Set an active pool for an address. - static setActivePool = ( - address: string, - poolId: string, - activePool: ActivePool | null - ): void => { - if (!this.activePools[address]) { - this.activePools[address] = {}; - } - this.activePools[address][poolId] = activePool; - }; - - // Set pool nominations for an address. - static setPoolNominations = ( - address: string, - poolId: string, - nominations: Nominations - ): void => { - if (!this.poolNominations[address]) { - this.poolNominations[address] = {}; - } - this.poolNominations[address][poolId] = nominations; - }; - - // Set unsub for an address and pool id. - static setUnsub = (address: string, poolId: string, unsub: VoidFn): void => { - if (!this.#unsubs[address]) { - this.#unsubs[address] = {}; - } - this.#unsubs[address][poolId] = unsub; - }; + // Gets all active pools for a provided network. + static getAllActivePools = (network: NetworkName) => + Object.fromEntries( + Object.entries(this.addressToPool).map(([addr, poolId]) => { + const activePoolAccount = SubscriptionsController.get( + network, + `activePool-${addr}-${poolId}` + ) as ActivePoolAccount; + + return [poolId, activePoolAccount?.activePool || null]; + }) + ); // ------------------------------------------------------ // Class helpers. diff --git a/packages/app/src/hooks/useActivePools/index.tsx b/packages/app/src/hooks/useActivePools/index.tsx index 2e52e3ac0..14973a490 100644 --- a/packages/app/src/hooks/useActivePools/index.tsx +++ b/packages/app/src/hooks/useActivePools/index.tsx @@ -18,14 +18,14 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { // Stores active pools. const [activePools, setActivePools] = useState( - ActivePoolsController.getActivePools(who) + ActivePoolsController.getActivePool(network, who) ); const activePoolsRef = useRef(activePools); // Store nominations of active pools. const [poolNominations, setPoolNominations] = useState( - ActivePoolsController.getPoolNominations(who) + ActivePoolsController.getPoolNominations(network, who) ); const poolNominationsRef = useRef(poolNominations); @@ -58,7 +58,8 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { }; // Get an active pool. - const getActivePools = (poolId: string) => activePools?.[poolId] || null; + const getActivePool = (poolId: string) => + activePools?.[Number(poolId)] || null; // Get an active pool's nominations. const getPoolNominations = (poolId: string) => @@ -73,12 +74,12 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { // Update state on account change. useEffect(() => { setStateWithRef( - ActivePoolsController.getActivePools(who), + ActivePoolsController.getActivePool(network, who), setActivePools, activePoolsRef ); setStateWithRef( - ActivePoolsController.getPoolNominations(who), + ActivePoolsController.getPoolNominations(network, who), setPoolNominations, poolNominationsRef ); @@ -91,7 +92,7 @@ export const useActivePools = ({ onCallback, who }: ActivePoolsProps) => { return { activePools, activePoolsRef, - getActivePools, + getActivePool, getPoolNominations, }; }; diff --git a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts index 25be11a0a..9f3410c3d 100644 --- a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts +++ b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts @@ -1,11 +1,15 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import type { Nominations } from 'contexts/Balances/types'; +import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; +import type { ActivePool, PoolRoles } from 'contexts/Pools/ActivePool/types'; import type { ActivePoolItem } from 'controllers/ActivePools/types'; import { ApiController } from 'controllers/Api'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { PapiApi } from 'model/Api/types'; import { combineLatest, type Subscription } from 'rxjs'; -import type { NetworkName, SystemChainId } from 'types'; +import type { AnyApi, NetworkName, SystemChainId } from 'types'; export class ActivePoolAccount implements Unsubscribable { // The associated network for this instance. @@ -15,40 +19,73 @@ export class ActivePoolAccount implements Unsubscribable { #sub: Subscription; // Active pool item - #pool: ActivePoolItem; + pool: ActivePoolItem; - constructor(network: NetworkName | SystemChainId, pool: ActivePoolItem) { + // Address associated with this pool. + address: string; + + // Active pool of the address. + activePool: ActivePool | null; + + // Active pool nominations. + poolNominations: Nominations; + + constructor( + network: NetworkName | SystemChainId, + address: string, + pool: ActivePoolItem + ) { this.#network = network; - this.#pool = pool; + this.pool = pool; + this.address = address; this.subscribe(); } subscribe = async (): Promise => { try { const { pApi } = ApiController.get(this.#network); + const { pApi: peopleApi } = ApiController.get( + `people-${this.#network}` as SystemChainId + ); const bestOrFinalized = 'best'; const sub = combineLatest([ pApi.query.NominationPools.BondedPools.watchValue( - this.#pool.id, + this.pool.id, bestOrFinalized ), pApi.query.NominationPools.RewardPools.watchValue( - this.#pool.id, + this.pool.id, bestOrFinalized ), pApi.query.System.Account.watchValue( - this.#pool.addresses.reward, + this.pool.addresses.reward, bestOrFinalized ), pApi.query.Staking.Nominators.watchValue( - this.#pool.addresses.stash, + this.pool.addresses.stash, bestOrFinalized ), ]).subscribe(async ([bondedPool, rewardPool, account, nominators]) => { - console.debug(bondedPool, rewardPool, account, nominators); + await this.handleActivePoolCallback( + peopleApi, + bondedPool, + rewardPool, + account + ); + this.handleNominatorsCallback(nominators); - // TODO: Implement callback. + if (this.activePool && this.poolNominations) { + document.dispatchEvent( + new CustomEvent('new-active-pool', { + detail: { + address: this.address, + pool: this.activePool, + nominations: this.poolNominations, + }, + }) + ); + } }); this.#sub = sub; @@ -57,6 +94,80 @@ export class ActivePoolAccount implements Unsubscribable { } }; + // Handle active pool callback. + handleActivePoolCallback = async ( + peopleApi: PapiApi, + bondedPool: AnyApi, + rewardPool: AnyApi, + account: AnyApi + ): Promise => { + const balance = account.data; + const rewardAccountBalance = balance?.free.toString(); + + if (peopleApi) { + // Fetch identities for roles and expand `bondedPool` state to store them. + // TODO: IdentitiesController migrate to PapiApi. + // bondedPool.roleIdentities = await IdentitiesController.fetch( + // peopleApi, + // this.getUniqueRoleAddresses(bondedPool.roles) + // ); + } + + const bondedPoolFormatted = { + points: bondedPool.points.toString(), + memberCounter: bondedPool.member_counter.toString(), + roles: bondedPool.roles, + roleIdentities: bondedPool.roleIdentities, + state: bondedPool.state.type, + }; + + const rewardPoolFormatted = { + lastRecordedRewardCounter: + rewardPool.last_recorded_reward_counter.toString(), + lastRecordedTotalPayouts: + rewardPool.last_recorded_total_payouts.toString(), + totalCommissionClaimed: rewardPool.total_commission_claimed.toString(), + totalCommissionPending: rewardPool.total_commission_pending.toString(), + totalRewardsClaimed: rewardPool.total_rewards_claimed.toString(), + }; + + // Only persist the active pool to class state (and therefore dispatch an event) if both the + // bonded pool and reward pool are returned. + if (bondedPool && rewardPool) { + const newPool = { + id: Number(this.pool.id), + addresses: this.pool.addresses, + bondedPool: bondedPoolFormatted, + rewardPool: rewardPoolFormatted, + rewardAccountBalance, + }; + + this.activePool = newPool; + } else { + // Invalid pools were returned. To signal pool was synced, set active pool to `null`. + this.activePool = null; + } + }; + + // Handle nominators callback. + handleNominatorsCallback = (nominators: AnyApi): void => { + const newNominations: Nominations = !nominators + ? defaultPoolNominations + : { + targets: nominators.targets, + submittedIn: nominators.submitted_in, + }; + this.poolNominations = newNominations; + }; + + // Gets unique role addresses from a bonded pool's `roles` record. + getUniqueRoleAddresses = (roles: PoolRoles): string[] => { + const roleAddresses: string[] = [ + ...new Set(Object.values(roles).filter((role) => role !== undefined)), + ]; + return roleAddresses; + }; + // Unsubscribe from class subscription. unsubscribe = (): void => { if (typeof this.#sub?.unsubscribe === 'function') { From 248076489070543c6c5d035b30ec640376064ef4 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 20:50:41 +0700 Subject: [PATCH 47/84] migrate `IdentitiesController` to `pApi` --- .../Validators/ValidatorEntries/index.tsx | 8 ++-- .../app/src/controllers/Identities/index.ts | 40 ++++++++++--------- .../src/hooks/useValidatorFilters/index.tsx | 4 +- .../ValidatorList/ValidatorItem/Utils.tsx | 4 +- .../src/model/Query/IdentityOfMulti/index.ts | 1 - .../app/src/model/Query/SuperOfMulti/index.ts | 28 +++++++++++++ .../Subscribe/ActivePoolAccount/index.ts | 10 ++--- 7 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 packages/app/src/model/Query/SuperOfMulti/index.ts diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index ac54c395c..57e4dd0a1 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -5,7 +5,7 @@ import { rmCommas, shuffle } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import type { AnyApi, Fn } from 'types'; +import type { AnyApi, Fn, SystemChainId } from 'types'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { useNetwork } from 'contexts/Network'; import { useApi } from 'contexts/Api'; @@ -322,11 +322,13 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { setAvgCommission(avg); // NOTE: validators are shuffled before committed to state. setValidators(shuffle(validatorEntries)); - + const { pApi: peoplePapiApi } = ApiController.get( + `people-${network}` as SystemChainId + ); if (peopleApi && peopleApiStatus === 'ready') { const addresses = validatorEntries.map(({ address }) => address); const { identities, supers } = await IdentitiesController.fetch( - peopleApi, + peoplePapiApi, addresses ); setValidatorIdentities(identities); diff --git a/packages/app/src/controllers/Identities/index.ts b/packages/app/src/controllers/Identities/index.ts index a4a4c14a7..43a2a7519 100644 --- a/packages/app/src/controllers/Identities/index.ts +++ b/packages/app/src/controllers/Identities/index.ts @@ -2,21 +2,23 @@ // SPDX-License-Identifier: GPL-3.0-only import type { AnyApi } from 'types'; -import type { ApiPromise } from '@polkadot/api'; import type { AnyJson } from '@w3ux/types'; +import type { PapiApi } from 'model/Api/types'; +import { IdentityOfMulti } from 'model/Query/IdentityOfMulti'; +import { SuperOfMulti } from 'model/Query/SuperOfMulti'; export class IdentitiesController { - static fetch = async (api: ApiPromise, addresses: string[]) => { + static fetch = async (pApi: PapiApi, addresses: string[]) => { // Fetches identities for addresses. const fetchBase = async () => { - const result = (await api.query.identity.identityOf.multi(addresses)).map( - (identity) => identity.toHuman() - ); + const addressesMulti: [string][] = addresses.map((address) => [address]); + const result = await new IdentityOfMulti(pApi, addressesMulti).fetch(); // Take identity data (first index) of results. - const data = result.map( - (resultArray: AnyJson | null) => resultArray?.[0] || null - ); + const data = + result?.map( + (resultArray: AnyJson | null) => resultArray?.[0] || null + ) || []; return Object.fromEntries( data @@ -27,26 +29,28 @@ export class IdentitiesController { // Fetch an array of super accounts and their identities. const fetchSupers = async () => { - const supersRaw = (await api.query.identity.superOf.multi(addresses)).map( - (superOf) => superOf.toHuman() - ); + const addressesMulti: [string][] = addresses.map((address) => [address]); + const supersRawMulti = await new SuperOfMulti( + pApi, + addressesMulti + ).fetch(); const supers = Object.fromEntries( - supersRaw + (supersRawMulti || []) .map((k, i) => [ addresses[i], { superOf: k, }, ]) - .filter(([, { superOf }]: AnyApi) => superOf !== null) + .filter(([, { superOf }]: AnyApi) => superOf !== undefined) ); - const superIdentities = ( - await api.query.identity.identityOf.multi( - Object.values(supers).map(({ superOf }: AnyApi) => superOf[0]) - ) - ).map((superIdentity) => superIdentity.toHuman()); + const superOfMulti: [string][] = Object.values(supers).map( + ({ superOf }: AnyApi) => [superOf[0]] + ); + const superIdentities = + (await new IdentityOfMulti(pApi, superOfMulti).fetch()) || []; // Take identity data (first index) of results. const data = superIdentities.map( diff --git a/packages/app/src/hooks/useValidatorFilters/index.tsx b/packages/app/src/hooks/useValidatorFilters/index.tsx index 82a659bc6..6e6ff6c58 100644 --- a/packages/app/src/hooks/useValidatorFilters/index.tsx +++ b/packages/app/src/hooks/useValidatorFilters/index.tsx @@ -211,7 +211,7 @@ export const useValidatorFilters = () => { const filteredList: AnyFilter = []; for (const validator of list) { const identity = validatorIdentities[validator.address] ?? ''; - const identityRaw = identity?.info?.display?.Raw ?? ''; + const identityRaw = identity?.info?.display?.value?.asText() ?? ''; const identityAsBytes = u8aToString(u8aUnwrapBytes(identityRaw)); const identitySearch = ( identityAsBytes === '' ? identityRaw : identityAsBytes @@ -219,7 +219,7 @@ export const useValidatorFilters = () => { const superIdentity = validatorSupers[validator.address] ?? null; const superIdentityRaw = - superIdentity?.identity?.info?.display?.Raw ?? ''; + superIdentity?.identity?.info?.display?.value?.asText() ?? ''; const superIdentityAsBytes = u8aToString( u8aUnwrapBytes(superIdentityRaw) ); diff --git a/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx b/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx index 184e6c34e..fe5b08a1e 100644 --- a/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx +++ b/packages/app/src/library/ValidatorList/ValidatorItem/Utils.tsx @@ -17,7 +17,7 @@ export const getIdentityDisplay = ( const superIdentity = _superIdentity?.identity ?? null; const superRaw = _superIdentity?.superOf?.[1]?.Raw ?? null; - const superDisplay = superIdentity?.info?.display?.Raw ?? null; + const superDisplay = superIdentity?.info?.display?.value?.asText() ?? null; // check if super raw has been encoded const superRawAsBytes = u8aToString(u8aUnwrapBytes(superRaw)); @@ -35,7 +35,7 @@ export const getIdentityDisplay = ( if (!foundSuper) { // cehck sub identity exists, get display.Raw if it does - const identity = _identity?.info?.display?.Raw ?? null; + const identity = _identity?.info?.display?.value?.asText() ?? null; // check if identity has been byte encoded const subIdentityAsBytes = u8aToString(u8aUnwrapBytes(identity)); diff --git a/packages/app/src/model/Query/IdentityOfMulti/index.ts b/packages/app/src/model/Query/IdentityOfMulti/index.ts index d2c8dc5c5..19ea4332e 100644 --- a/packages/app/src/model/Query/IdentityOfMulti/index.ts +++ b/packages/app/src/model/Query/IdentityOfMulti/index.ts @@ -3,7 +3,6 @@ import type { PapiApi } from 'model/Api/types'; -// TODO: Hook up and test. export class IdentityOfMulti { #pApi: PapiApi; diff --git a/packages/app/src/model/Query/SuperOfMulti/index.ts b/packages/app/src/model/Query/SuperOfMulti/index.ts new file mode 100644 index 000000000..3ae9eb11c --- /dev/null +++ b/packages/app/src/model/Query/SuperOfMulti/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class SuperOfMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + try { + const result = await this.#pApi.query.Identity.SuperOf.getValues( + this.#addresses + ); + return result; + } catch (e) { + // Silently fail. + } + + return null; + } +} diff --git a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts index 9f3410c3d..2283f4b86 100644 --- a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts +++ b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts @@ -6,6 +6,7 @@ import { defaultPoolNominations } from 'contexts/Pools/ActivePool/defaults'; import type { ActivePool, PoolRoles } from 'contexts/Pools/ActivePool/types'; import type { ActivePoolItem } from 'controllers/ActivePools/types'; import { ApiController } from 'controllers/Api'; +import { IdentitiesController } from 'controllers/Identities'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; import type { PapiApi } from 'model/Api/types'; import { combineLatest, type Subscription } from 'rxjs'; @@ -106,11 +107,10 @@ export class ActivePoolAccount implements Unsubscribable { if (peopleApi) { // Fetch identities for roles and expand `bondedPool` state to store them. - // TODO: IdentitiesController migrate to PapiApi. - // bondedPool.roleIdentities = await IdentitiesController.fetch( - // peopleApi, - // this.getUniqueRoleAddresses(bondedPool.roles) - // ); + bondedPool.roleIdentities = await IdentitiesController.fetch( + peopleApi, + this.getUniqueRoleAddresses(bondedPool.roles) + ); } const bondedPoolFormatted = { From e6446ffb2a5031967e3a2be4a4311cddb81bc2b5 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 21:12:15 +0700 Subject: [PATCH 48/84] migrate validator entries queries --- .../Validators/ValidatorEntries/index.tsx | 36 ++++++++++++------- .../model/Query/SessionValidators/index.ts | 23 ++++++++++++ .../src/model/Query/ValidatorsMulti/index.ts | 28 +++++++++++++++ 3 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 packages/app/src/model/Query/SessionValidators/index.ts create mode 100644 packages/app/src/model/Query/ValidatorsMulti/index.ts diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index 57e4dd0a1..f133ae9eb 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -36,6 +36,8 @@ import type { AnyJson, Sync } from '@w3ux/types'; import { Validators } from 'model/Entries/Validators'; import { ApiController } from 'controllers/Api'; import { perbillToPercent } from 'library/Utils'; +import { SessionValidators } from 'model/Query/SessionValidators'; +import { ValidatorsMulti } from 'model/Query/ValidatorsMulti'; export const ValidatorsContext = createContext( defaultValidatorsContext @@ -340,11 +342,11 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Subscribe to active session validators. const fetchSessionValidators = async () => { - if (!api || !isReady) { + if (!isReady) { return; } - const sessionValidatorsRaw: AnyApi = await api.query.session.validators(); - setSessionValidators(sessionValidatorsRaw.toHuman()); + const { pApi } = ApiController.get(network); + setSessionValidators(await new SessionValidators(pApi).fetch()); }; // Subscribe to active parachain validators. @@ -368,21 +370,29 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } const v: string[] = []; + const vMulti: [string][] = []; for (const { address } of addresses) { v.push(address); + vMulti.push([address]); } - const results = await api.query.staking.validators.multi(v); + + const { pApi } = ApiController.get(network); + const resultsMulti = + (await new ValidatorsMulti(pApi, vMulti).fetch()) || []; const formatted: Validator[] = []; - for (let i = 0; i < results.length; i++) { - const prefs: AnyApi = results[i].toHuman(); - formatted.push({ - address: v[i], - prefs: { - commission: prefs?.commission.replace(/%/g, '') ?? '0', - blocked: prefs.blocked, - }, - }); + for (let i = 0; i < resultsMulti.length; i++) { + const prefs: AnyApi = resultsMulti[i]; + + if (prefs) { + formatted.push({ + address: v[i], + prefs: { + commission: Number(perbillToPercent(prefs.commission).toString()), + blocked: prefs.blocked, + }, + }); + } } return formatted; }; diff --git a/packages/app/src/model/Query/SessionValidators/index.ts b/packages/app/src/model/Query/SessionValidators/index.ts new file mode 100644 index 000000000..6b35d998c --- /dev/null +++ b/packages/app/src/model/Query/SessionValidators/index.ts @@ -0,0 +1,23 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class SessionValidators { + #pApi: PapiApi; + + constructor(pApi: PapiApi) { + this.#pApi = pApi; + } + + // Fetch network constants. + async fetch() { + try { + const result = await this.#pApi.query.Session.Validators.getValue(); + return result; + } catch (e) { + // Silently fail. + } + return []; + } +} diff --git a/packages/app/src/model/Query/ValidatorsMulti/index.ts b/packages/app/src/model/Query/ValidatorsMulti/index.ts new file mode 100644 index 000000000..66c17bc8d --- /dev/null +++ b/packages/app/src/model/Query/ValidatorsMulti/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ValidatorsMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, addresses: [string][]) { + this.#pApi = pApi; + this.#addresses = addresses; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.Validators.getValues( + this.#addresses + ); + return result; + } catch (e) { + // Silently fail. + } + + return null; + } +} From 69e34a0749d5615d251a8f5f984a58edb5d6f2cf Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 18 Nov 2024 21:56:11 +0700 Subject: [PATCH 49/84] add `ParaSessionAccounts` query --- .../Validators/ValidatorEntries/index.tsx | 35 ++++++------------- packages/app/src/model/Query/Era/index.ts | 1 - .../model/Query/ParaSessionAccounts/index.ts | 29 +++++++++++++++ 3 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 packages/app/src/model/Query/ParaSessionAccounts/index.ts diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index f133ae9eb..bf007d6f8 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -4,8 +4,8 @@ import { rmCommas, shuffle } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; -import { createContext, useContext, useEffect, useRef, useState } from 'react'; -import type { AnyApi, Fn, SystemChainId } from 'types'; +import { createContext, useContext, useEffect, useState } from 'react'; +import type { AnyApi, SystemChainId } from 'types'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { useNetwork } from 'contexts/Network'; import { useApi } from 'contexts/Api'; @@ -38,6 +38,7 @@ import { ApiController } from 'controllers/Api'; import { perbillToPercent } from 'library/Utils'; import { SessionValidators } from 'model/Query/SessionValidators'; import { ValidatorsMulti } from 'model/Query/ValidatorsMulti'; +import { ParaSessionAccounts } from 'model/Query/ParaSessionAccounts'; export const ValidatorsContext = createContext( defaultValidatorsContext @@ -83,9 +84,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { [] ); - // Stores unsub object for para session. - const sessionParaUnsub = useRef(); - // Stores the average network commission rate. const [avgCommission, setAvgCommission] = useState(0); @@ -350,16 +348,13 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { }; // Subscribe to active parachain validators. - const subscribeParachainValidators = async () => { - if (!api || !isReady) { - return; - } - const unsub: AnyApi = await api.query.paraSessionInfo.accountKeys( - earliestStoredSession.toString(), - (v: AnyApi) => { - setSessionParaValidators(v.toHuman()); - sessionParaUnsub.current = unsub; - } + const getParachainValidators = async () => { + const { pApi } = ApiController.get(network); + setSessionParaValidators( + await new ParaSessionAccounts( + pApi, + earliestStoredSession.toString() + ).fetch() ); }; @@ -591,18 +586,10 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetch parachain session validators when `earliestStoredSession` ready. useEffectIgnoreInitial(() => { if (isReady && earliestStoredSession.isGreaterThan(0)) { - subscribeParachainValidators(); + getParachainValidators(); } }, [isReady, earliestStoredSession]); - // Unsubscribe on network change and component unmount. - useEffect(() => { - sessionParaUnsub.current?.(); - return () => { - sessionParaUnsub.current?.(); - }; - }, [network]); - return ( Date: Tue, 19 Nov 2024 11:22:12 +0700 Subject: [PATCH 50/84] add `PoolMemberBatchEvent` and migrate pool member batches --- .../src/contexts/Pools/PoolMembers/index.tsx | 105 ++++++++---------- .../src/controllers/Subscriptions/types.ts | 2 + .../model/Subscribe/PoolMembersMulti/index.ts | 82 ++++++++++++++ .../model/Subscribe/PoolMembersMulti/types.ts | 15 +++ packages/app/src/types.ts | 2 + 5 files changed, 146 insertions(+), 60 deletions(-) create mode 100644 packages/app/src/model/Subscribe/PoolMembersMulti/index.ts create mode 100644 packages/app/src/model/Subscribe/PoolMembersMulti/types.ts diff --git a/packages/app/src/contexts/Pools/PoolMembers/index.tsx b/packages/app/src/contexts/Pools/PoolMembers/index.tsx index 02cf7dcdd..87ffa61dc 100644 --- a/packages/app/src/contexts/Pools/PoolMembers/index.tsx +++ b/packages/app/src/contexts/Pools/PoolMembers/index.tsx @@ -5,7 +5,7 @@ import { setStateWithRef } from '@w3ux/utils'; import type { ReactNode } from 'react'; import { createContext, useContext, useRef, useState } from 'react'; import { usePlugins } from 'contexts/Plugins'; -import type { AnyApi, AnyMetaBatch, Fn, MaybeAddress } from 'types'; +import type { AnyMetaBatch, MaybeAddress } from 'types'; import { useEffectIgnoreInitial } from '@w3ux/hooks'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -13,6 +13,10 @@ import { useApi } from '../../Api'; import { defaultPoolMembers } from './defaults'; import type { PoolMember, PoolMemberContext } from './types'; import type { Sync } from '@w3ux/types'; +import { PoolMembersMulti } from 'model/Subscribe/PoolMembersMulti'; +import { SubscriptionsController } from 'controllers/Subscriptions'; +import { useEventListener } from 'usehooks-ts'; +import { isCustomEvent } from 'controllers/utils'; export const PoolMembersContext = createContext(defaultPoolMembers); @@ -20,8 +24,8 @@ export const PoolMembersContext = export const usePoolMembers = () => useContext(PoolMembersContext); export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); - const { api, isReady } = useApi(); const { pluginEnabled } = usePlugins(); const { activeAccount } = useActiveAccounts(); @@ -36,8 +40,8 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { useState({}); const poolMembersMetaBatchesRef = useRef(poolMembersMetaBatches); - // Stores the meta batch subscriptions for pool lists. - const poolMembersSubs = useRef>({}); + // Stores the meta batch subscription keys for pool lists. + const poolMembersSubs = useRef([]); // Update poolMembersApi fetched status. const setFetchedPoolMembersApi = (status: Sync) => { @@ -72,32 +76,18 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { }; const unsubscribeAndResetMeta = () => { - Object.values(poolMembersSubs.current).map((batch: Fn[]) => - Object.entries(batch).map(([, v]) => v()) + Object.values(poolMembersSubs.current).map((key: string) => + SubscriptionsController.remove(network, `poolMembersBatch-${key}`) ); setStateWithRef({}, setPoolMembersMetaBatch, poolMembersMetaBatchesRef); }; - /* - Fetches a new batch of pool member metadata. - structure: - { - key: { - [ - { - identities: [], - super_identities: [], - } - ] - }, - }; - */ const fetchPoolMembersMetaBatch = async ( key: string, p: AnyMetaBatch, refetch = false ) => { - if (!isReady || !api) { + if (!isReady) { return; } if (!p.length) { @@ -120,15 +110,16 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { poolMembersMetaBatchesRef ); - if (poolMembersSubs.current[key] !== undefined) { - for (const unsub of poolMembersSubs.current[key]) { - unsub(); - } + if (key in poolMembersSubs.current) { + SubscriptionsController.remove(network, `poolMembersBatch-${key}`); + poolMembersSubs.current = poolMembersSubs.current.filter( + (item) => item !== key + ); } } // aggregate member addresses - const addresses = []; + const addresses: string[] = []; for (const { who } of p) { addresses.push(who); } @@ -143,32 +134,15 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { poolMembersMetaBatchesRef ); - const subscribeToPoolMembers = async (addr: string[]) => { - const unsub = await api.query.nominationPools.poolMembers.multi( - addr, - (_pools) => { - const pools = []; - for (const _pool of _pools) { - pools.push(_pool.toHuman()); - } - const updated = Object.assign(poolMembersMetaBatchesRef.current); - updated[key].poolMembers = pools; - setStateWithRef( - { ...updated }, - setPoolMembersMetaBatch, - poolMembersMetaBatchesRef - ); - } - ); - return unsub; - }; - - // initiate subscriptions - await Promise.all([subscribeToPoolMembers(addresses)]).then( - (unsubs: Fn[]) => { - addMetaBatchUnsubs(key, unsubs); - } + // initialise subscription + SubscriptionsController.set( + network, + `poolMembersBatch-${key}`, + new PoolMembersMulti(network, key, addresses) ); + + // Record key. + poolMembersSubs.current.push(key); }; // Removes a member from the member list and updates state. Requires subscan to be enabled. @@ -178,17 +152,28 @@ export const PoolMembersProvider = ({ children }: { children: ReactNode }) => { } }; - /* - * Helper: to add mataBatch unsubs by key. - */ - const addMetaBatchUnsubs = (key: string, unsubs: Fn[]) => { - const subs = poolMembersSubs.current; - const sub = subs[key] ?? []; - sub.push(...unsubs); - subs[key] = sub; - poolMembersSubs.current = subs; + // Handle new pool members batch event. + const handleNewPoolMembersBatch = (e: Event) => { + if (isCustomEvent(e)) { + const { key, poolMembers } = e.detail; + + const updated = Object.assign(poolMembersMetaBatchesRef.current); + updated[key].poolMembers = poolMembers; + setStateWithRef( + { ...updated }, + setPoolMembersMetaBatch, + poolMembersMetaBatchesRef + ); + } }; + const documentRef = useRef(document); + useEventListener( + 'new-pool-members-batch', + handleNewPoolMembersBatch, + documentRef + ); + return ( => { + try { + const { pApi } = ApiController.get(this.#network); + if (pApi && this.#sub === undefined) { + const bestOrFinalized = 'best'; + + const sub = combineLatest( + this.#addresses.map((address) => + pApi.query.NominationPools.PoolMembers.watchValue( + address, + bestOrFinalized + ) + ) + ).subscribe((results) => { + const formatted = results.map((result) => ({ + lastRecordedRewardCounter: + result.last_recorded_reward_counter.toString(), + points: result.points.toString(), + poolId: result.pool_id.toString(), + unbondingEras: Object.fromEntries( + result.unbonding_eras.map(([key, value]: [number, bigint]) => [ + key.toString(), + value.toString(), + ]) + ), + })); + + const detail: PoolMemberBatchEvent = { + key: this.#key, + addresses: this.#addresses, + poolMembers: formatted, + }; + + document.dispatchEvent( + new CustomEvent('new-pool-members-batch', { + detail, + }) + ); + }); + + this.#sub = sub; + } + } catch (e) { + // Subscription failed. + } + }; + + // Unsubscribe from class subscription. + unsubscribe = (): void => { + if (typeof this.#sub?.unsubscribe === 'function') { + this.#sub.unsubscribe(); + } + }; +} diff --git a/packages/app/src/model/Subscribe/PoolMembersMulti/types.ts b/packages/app/src/model/Subscribe/PoolMembersMulti/types.ts new file mode 100644 index 000000000..ed7b85514 --- /dev/null +++ b/packages/app/src/model/Subscribe/PoolMembersMulti/types.ts @@ -0,0 +1,15 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export interface PoolMemberBatchEvent { + key: string; + addresses: string[]; + poolMembers: Record< + number, + { + poolId: string; + points: string; + unbondingEras: Record; + } + >; +} diff --git a/packages/app/src/types.ts b/packages/app/src/types.ts index 22ae59ee6..3c4c47edc 100644 --- a/packages/app/src/types.ts +++ b/packages/app/src/types.ts @@ -24,6 +24,7 @@ import type { BondedAccount } from 'contexts/Bonded/types'; import type { FastUnstakeConfigResult } from 'model/Subscribe/FastUnstakeConfig/types'; import type { FastUnstakeQueueResult } from 'contexts/FastUnstake/types'; import type { AccountProxiesEvent } from 'model/Subscribe/AccountProxies/types'; +import type { PoolMemberBatchEvent } from 'model/Subscribe/PoolMembersMulti/types'; declare global { interface Window { @@ -46,6 +47,7 @@ declare global { stakingMetrics: APIStakingMetrics; }>; 'new-active-pool': CustomEvent; + 'new-pool-members-batch': CustomEvent; 'new-fast-unstake-config': CustomEvent; 'new-fast-unstake-deposit': CustomEvent; 'new-account-proxies': CustomEvent; From 6209042d259c68a9d336359347061fbe14f78e93 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 19 Nov 2024 11:43:12 +0700 Subject: [PATCH 51/84] migrate `ValidatorEntries` multi queries --- .../Validators/ValidatorEntries/index.tsx | 40 ++++++++++--------- .../Query/ErasRewardPointsMulti/index.ts | 28 +++++++++++++ .../Query/ErasValidatorRewardMulti/index.ts | 29 ++++++++++++++ 3 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 packages/app/src/model/Query/ErasRewardPointsMulti/index.ts create mode 100644 packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index bf007d6f8..e3aea5fdd 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -1,7 +1,7 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { rmCommas, shuffle } from '@w3ux/utils'; +import { shuffle } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; import { createContext, useContext, useEffect, useState } from 'react'; @@ -39,6 +39,8 @@ import { perbillToPercent } from 'library/Utils'; import { SessionValidators } from 'model/Query/SessionValidators'; import { ValidatorsMulti } from 'model/Query/ValidatorsMulti'; import { ParaSessionAccounts } from 'model/Query/ParaSessionAccounts'; +import { ErasRewardPointsMulti } from 'model/Query/ErasRewardPointsMulti'; +import { ErasValidatorReward } from 'model/Query/ErasValidatorRewardMulti'; export const ValidatorsContext = createContext( defaultValidatorsContext @@ -50,7 +52,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { isReady, - api, peopleApi, peopleApiStatus, consts: { historyDepth }, @@ -118,11 +119,11 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { } return { - total: rmCommas(result.total), + total: result.total.toString(), individual: Object.fromEntries( - Object.entries(result.individual).map(([key, value]) => [ + result.individual.map(([key, value]: [number, string]) => [ key, - rmCommas(value as string), + (value as string).toString(), ]) ), }; @@ -148,9 +149,10 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetches era reward points for eligible eras. const fetchErasRewardPoints = async () => { + const { pApi } = ApiController.get(network); if ( activeEra.index.isZero() || - !api || + !pApi || erasRewardPointsFetched !== 'unsynced' ) { return; @@ -170,12 +172,9 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { let erasProcessed = new BigNumber(0); // Iterate eras and process reward points. - const calls = []; const eras = []; do { - calls.push(api.query.staking.erasRewardPoints(currentEra.toString())); eras.push(currentEra); - currentEra = currentEra.minus(1); erasProcessed = erasProcessed.plus(1); } while ( @@ -183,11 +182,14 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { erasProcessed.isLessThan(totalEras) ); + const erasMulti: [string][] = eras.map((e) => [e.toString()]); + const results = await new ErasRewardPointsMulti(pApi, erasMulti).fetch(); + // Make calls and format reward point results. const newErasRewardPoints: ErasRewardPoints = {}; let i = 0; - for (const result of await Promise.all(calls)) { - const formatted = processEraRewardPoints(result.toHuman(), eras[i]); + for (const result of results) { + const formatted = processEraRewardPoints(result, eras[i]); if (formatted) { newErasRewardPoints[eras[i].toString()] = formatted; } @@ -279,7 +281,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetches and formats the active validator set, and derives metrics from the result. const fetchValidators = async () => { - if (!isReady || !api || validatorsFetched !== 'unsynced') { + if (!isReady || validatorsFetched !== 'unsynced') { return; } setValidatorsFetched('syncing'); @@ -360,7 +362,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Fetches prefs for a list of validators. const fetchValidatorPrefs = async (addresses: ValidatorAddresses) => { - if (!addresses.length || !api) { + if (!addresses.length) { return null; } @@ -485,7 +487,9 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { // Gets average validator reward for provided number of days. const getAverageEraValidatorReward = async () => { - if (!api || !isReady || activeEra.index.isZero()) { + const { pApi } = ApiController.get(network); + + if (!pApi || !isReady || activeEra.index.isZero()) { setAverageEraValidatorReward({ days: 0, reward: new BigNumber(0), @@ -510,12 +514,12 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { thisEra = thisEra.minus(1); } while (thisEra.gte(endEra)); - const validatorEraRewards = - await api.query.staking.erasValidatorReward.multi(eras); + const erasMulti: [string][] = eras.map((e) => [e.toString()]); + const results = await new ErasValidatorReward(pApi, erasMulti).fetch(); - const reward = validatorEraRewards + const reward = results .map((v) => { - const value = new BigNumber(v.toString() === '' ? 0 : v.toString()); + const value = new BigNumber(!v ? 0 : v.toString()); if (value.isNaN()) { return new BigNumber(0); } diff --git a/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts new file mode 100644 index 000000000..14ac6a518 --- /dev/null +++ b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasRewardPointsMulti { + #pApi: PapiApi; + + #eras: [string][]; + + constructor(pApi: PapiApi, eras: [string][]) { + this.#pApi = pApi; + this.#eras = eras; + } + + async fetch() { + try { + const results = await this.#pApi.query.Staking.ErasRewardPoints.getValues( + this.#eras + ); + return results; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts new file mode 100644 index 000000000..61894f746 --- /dev/null +++ b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts @@ -0,0 +1,29 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasValidatorReward { + #pApi: PapiApi; + + #eras: [string][]; + + constructor(pApi: PapiApi, eras: [string][]) { + this.#pApi = pApi; + this.#eras = eras; + } + + async fetch() { + try { + const results = + await this.#pApi.query.Staking.ErasValidatorReward.getValues( + this.#eras + ); + return results; + } catch (e) { + // Silently fail. + } + + return []; + } +} From 116be101459f4a0cce304d308749073b39ec0d6d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 19 Nov 2024 12:07:44 +0700 Subject: [PATCH 52/84] migrate `ClaimedRewards`, `BondedMulti` queries --- packages/app/src/contexts/Payouts/index.tsx | 22 +++++++++---- .../app/src/model/Query/BondedMulti/index.ts | 28 ++++++++++++++++ .../src/model/Query/ClaimedRewards/index.ts | 32 +++++++++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 packages/app/src/model/Query/BondedMulti/index.ts create mode 100644 packages/app/src/model/Query/ClaimedRewards/index.ts diff --git a/packages/app/src/contexts/Payouts/index.tsx b/packages/app/src/contexts/Payouts/index.tsx index 5ef3138f6..31e5b57cf 100644 --- a/packages/app/src/contexts/Payouts/index.tsx +++ b/packages/app/src/contexts/Payouts/index.tsx @@ -24,6 +24,9 @@ import { setLocalEraExposure, setLocalUnclaimedPayouts, } from './Utils'; +import { BondedMulti } from 'model/Query/BondedMulti'; +import { ApiController } from 'controllers/Api'; +import { ClaimedRewards } from 'model/Query/ClaimedRewards'; const worker = new Worker(); @@ -136,6 +139,7 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { // Start pending payout process once exposure data is fetched. const getUnclaimedPayouts = async () => { + const { pApi } = ApiController.get(network); if (!api || !activeAccount) { return; } @@ -163,11 +167,15 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { ); // Fetch controllers in order to query ledgers. - const bondedResults = - await api.query.staking.bonded.multi(uniqueValidators); + const uniqueValidatorsMulti: [string][] = uniqueValidators.map((v) => [v]); + const bondedResultsMulti = await new BondedMulti( + pApi, + uniqueValidatorsMulti + ).fetch(); + const validatorControllers: Record = {}; - for (let i = 0; i < bondedResults.length; i++) { - const ctlr = bondedResults[i].unwrapOr(null); + for (let i = 0; i < bondedResultsMulti.length; i++) { + const ctlr = bondedResultsMulti[i] || null; if (ctlr) { validatorControllers[uniqueValidators[i]] = ctlr; } @@ -186,18 +194,18 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { const results = await Promise.all( unclaimedRewardsEntries.map(([era, v]) => - api.query.staking.claimedRewards(era, v) + new ClaimedRewards(pApi, era, v).fetch() ) ); for (let i = 0; i < results.length; i++) { - const pages = results[i].toHuman() || []; + const pages = results[i] || []; const era = unclaimedRewardsEntries[i][0]; const validator = unclaimedRewardsEntries[i][1]; const exposure = getLocalEraExposure(network, era, activeAccount); const exposedPage = exposure?.[validator]?.exposedPage !== undefined - ? String(exposure[validator].exposedPage) + ? Number(exposure[validator].exposedPage) : undefined; // Add to `unclaimedRewards` if payout page has not yet been claimed. diff --git a/packages/app/src/model/Query/BondedMulti/index.ts b/packages/app/src/model/Query/BondedMulti/index.ts new file mode 100644 index 000000000..d56d994d9 --- /dev/null +++ b/packages/app/src/model/Query/BondedMulti/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class BondedMulti { + #pApi: PapiApi; + + #addresses: [string][]; + + constructor(pApi: PapiApi, eras: [string][]) { + this.#pApi = pApi; + this.#addresses = eras; + } + + async fetch() { + try { + const results = await this.#pApi.query.Staking.Bonded.getValues( + this.#addresses + ); + return results; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ClaimedRewards/index.ts b/packages/app/src/model/Query/ClaimedRewards/index.ts new file mode 100644 index 000000000..35a90cfdf --- /dev/null +++ b/packages/app/src/model/Query/ClaimedRewards/index.ts @@ -0,0 +1,32 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ClaimedRewards { + #pApi: PapiApi; + + #era: string; + + #address: string; + + constructor(pApi: PapiApi, era: string, address: string) { + this.#pApi = pApi; + this.#era = era; + this.#address = address; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.ClaimedRewards.getValue( + this.#era, + this.#address + ); + return result; + } catch (e) { + // Silently fail. + } + + return undefined; + } +} From acab254e3a6a22d2271e26a9b9acdf3cf1a7c7c0 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 19 Nov 2024 12:21:54 +0700 Subject: [PATCH 53/84] migrate `Payouts` queries --- packages/app/src/contexts/Payouts/index.tsx | 35 ++++++++++++------- .../src/model/Query/ErasRewardPoints/index.ts | 28 +++++++++++++++ .../model/Query/ErasValidatorReward/index.ts | 27 ++++++++++++++ .../src/model/Query/ValidatorPrefs/index.ts | 32 +++++++++++++++++ 4 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 packages/app/src/model/Query/ErasRewardPoints/index.ts create mode 100644 packages/app/src/model/Query/ErasValidatorReward/index.ts create mode 100644 packages/app/src/model/Query/ValidatorPrefs/index.ts diff --git a/packages/app/src/contexts/Payouts/index.tsx b/packages/app/src/contexts/Payouts/index.tsx index 31e5b57cf..e8cd04819 100644 --- a/packages/app/src/contexts/Payouts/index.tsx +++ b/packages/app/src/contexts/Payouts/index.tsx @@ -8,7 +8,7 @@ import { useApi } from 'contexts/Api'; import type { AnyApi } from 'types'; import type { AnyJson, Sync } from '@w3ux/types'; import Worker from 'workers/stakers?worker'; -import { rmCommas, setStateWithRef } from '@w3ux/utils'; +import { setStateWithRef } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -27,6 +27,10 @@ import { import { BondedMulti } from 'model/Query/BondedMulti'; import { ApiController } from 'controllers/Api'; import { ClaimedRewards } from 'model/Query/ClaimedRewards'; +import { ErasValidatorReward } from 'model/Query/ErasValidatorReward'; +import { ErasRewardPoints } from 'model/Query/ErasRewardPoints'; +import { ValidatorPrefs } from 'model/Query/ValidatorPrefs'; +import { perbillToPercent } from 'library/Utils'; const worker = new Worker(); @@ -238,10 +242,10 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { if (validators.length > 0) { calls.push( Promise.all([ - api.query.staking.erasValidatorReward(era), - api.query.staking.erasRewardPoints(era), + new ErasValidatorReward(pApi, era).fetch(), + new ErasRewardPoints(pApi, era).fetch(), ...validators.map((validator: AnyJson) => - api.query.staking.erasValidatorPrefs(era, validator) + new ValidatorPrefs(pApi, era, validator).fetch() ), ]) ); @@ -252,18 +256,22 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { // `unclaimed`: Record>. const unclaimed: UnclaimedPayouts = {}; let i = 0; - for (const [reward, points, ...prefs] of await Promise.all(calls)) { + for (const [reward, eraRewardPoints, ...prefs] of await Promise.all( + calls + )) { const era = Object.keys(unclaimedByEra)[i]; - const eraTotalPayout = new BigNumber(rmCommas(reward.toHuman())); - const eraRewardPoints = points.toHuman(); + const eraTotalPayout = new BigNumber(reward.toString()); const unclaimedValidators = unclaimedByEra[era]; let j = 0; for (const pref of prefs) { - const eraValidatorPrefs = pref.toHuman(); + const eraValidatorPrefs = { + commission: pref.commission, + blocked: pref.blocked, + }; const commission = new BigNumber( - eraValidatorPrefs.commission.replace(/%/g, '') - ).multipliedBy(0.01); + perbillToPercent(eraValidatorPrefs.commission) + ); // Get validator from era exposure data. Falls back no null if it cannot be found. const validator = unclaimedValidators?.[j] || ''; @@ -281,11 +289,14 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { // Calculate the validator's share of total era payout. const totalRewardPoints = new BigNumber( - rmCommas(eraRewardPoints.total) + eraRewardPoints.total.toString() ); const validatorRewardPoints = new BigNumber( - rmCommas(eraRewardPoints.individual?.[validator] || '0') + eraRewardPoints.individual.find( + ([v]: [string]) => v === validator + )?.[1] || '0' ); + const avail = eraTotalPayout .multipliedBy(validatorRewardPoints) .dividedBy(totalRewardPoints); diff --git a/packages/app/src/model/Query/ErasRewardPoints/index.ts b/packages/app/src/model/Query/ErasRewardPoints/index.ts new file mode 100644 index 000000000..149910abb --- /dev/null +++ b/packages/app/src/model/Query/ErasRewardPoints/index.ts @@ -0,0 +1,28 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasRewardPoints { + #pApi: PapiApi; + + #era: string; + + constructor(pApi: PapiApi, era: string) { + this.#pApi = pApi; + this.#era = era; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.ErasRewardPoints.getValue( + this.#era + ); + return result; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ErasValidatorReward/index.ts b/packages/app/src/model/Query/ErasValidatorReward/index.ts new file mode 100644 index 000000000..fc6a9b9bf --- /dev/null +++ b/packages/app/src/model/Query/ErasValidatorReward/index.ts @@ -0,0 +1,27 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ErasValidatorReward { + #pApi: PapiApi; + + #era: string; + + constructor(pApi: PapiApi, era: string) { + this.#pApi = pApi; + this.#era = era; + } + + async fetch() { + try { + const result = + await this.#pApi.query.Staking.ErasValidatorReward.getValue(this.#era); + return result; + } catch (e) { + // Silently fail. + } + + return []; + } +} diff --git a/packages/app/src/model/Query/ValidatorPrefs/index.ts b/packages/app/src/model/Query/ValidatorPrefs/index.ts new file mode 100644 index 000000000..9554a8246 --- /dev/null +++ b/packages/app/src/model/Query/ValidatorPrefs/index.ts @@ -0,0 +1,32 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { PapiApi } from 'model/Api/types'; + +export class ValidatorPrefs { + #pApi: PapiApi; + + #era: string; + + #address: string; + + constructor(pApi: PapiApi, era: string, address: string) { + this.#pApi = pApi; + this.#era = era; + this.#address = address; + } + + async fetch() { + try { + const result = await this.#pApi.query.Staking.ErasValidatorPrefs.getValue( + this.#era, + this.#address + ); + return result; + } catch (e) { + // Silently fail. + } + + return undefined; + } +} From d60e7848ddd5a76cf634f22525db19d55b9ebb3f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 19 Nov 2024 14:55:29 +0700 Subject: [PATCH 54/84] fixes --- packages/app/src/contexts/Payouts/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/app/src/contexts/Payouts/index.tsx b/packages/app/src/contexts/Payouts/index.tsx index e8cd04819..a61013bca 100644 --- a/packages/app/src/contexts/Payouts/index.tsx +++ b/packages/app/src/contexts/Payouts/index.tsx @@ -42,7 +42,7 @@ export const usePayouts = () => useContext(PayoutsContext); export const PayoutsProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); - const { api, consts, activeEra } = useApi(); + const { consts, activeEra } = useApi(); const { activeAccount } = useActiveAccounts(); const { isNominating, fetchEraStakers } = useStaking(); const { maxExposurePageSize } = consts; @@ -144,7 +144,7 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { // Start pending payout process once exposure data is fetched. const getUnclaimedPayouts = async () => { const { pApi } = ApiController.get(network); - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return; } @@ -301,7 +301,7 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { .multipliedBy(validatorRewardPoints) .dividedBy(totalRewardPoints); - const valCut = commission.multipliedBy(avail); + const valCut = commission.multipliedBy(0.01).multipliedBy(avail); const unclaimedPayout = total.isZero() ? new BigNumber(0) @@ -309,7 +309,8 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { .minus(valCut) .multipliedBy(staked) .dividedBy(total) - .plus(isValidator ? valCut : 0); + .plus(isValidator ? valCut : 0) + .integerValue(BigNumber.ROUND_DOWN); if (!unclaimedPayout.isZero()) { unclaimed[era] = { From ae8772e0093cf16405e05297d47571cdb7692b2b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 20 Nov 2024 17:05:17 +0700 Subject: [PATCH 55/84] fix --- packages/app/src/hooks/useActiveBalances/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/hooks/useActiveBalances/index.tsx b/packages/app/src/hooks/useActiveBalances/index.tsx index 491bfe47c..0b5c91afc 100644 --- a/packages/app/src/hooks/useActiveBalances/index.tsx +++ b/packages/app/src/hooks/useActiveBalances/index.tsx @@ -62,7 +62,7 @@ export const useActiveBalances = ({ // Gets an active balance's locks. const getLocks = (address: MaybeAddress): BalanceLocks => { if (address) { - const maybeLocks = activeBalances[address]?.balances.locks; + const maybeLocks = activeBalances[address]?.balances?.locks; if (maybeLocks) { return { locks: maybeLocks, maxLock: getMaxLock(maybeLocks) }; } From 3bbc829690093333e0b62f614142d9b3111b3263 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 20 Nov 2024 17:13:28 +0700 Subject: [PATCH 56/84] fix --- packages/app/src/hooks/useActiveBalances/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/hooks/useActiveBalances/index.tsx b/packages/app/src/hooks/useActiveBalances/index.tsx index 0b5c91afc..341e10cca 100644 --- a/packages/app/src/hooks/useActiveBalances/index.tsx +++ b/packages/app/src/hooks/useActiveBalances/index.tsx @@ -43,7 +43,7 @@ export const useActiveBalances = ({ // Gets an active balance's balance. const getBalance = (address: MaybeAddress) => { if (address) { - const maybeBalance = activeBalances[address]?.balances.balance; + const maybeBalance = activeBalances[address]?.balances?.balance; if (maybeBalance) { return maybeBalance; } From 77fe892ae0de709faba08fa05464f2b95f355a9d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 20 Nov 2024 20:38:29 +0700 Subject: [PATCH 57/84] prepare tx hooks --- packages/app/package.json | 6 +- .../app/src/hooks/useBatchCallPapi/index.tsx | 49 ++ .../src/hooks/useBondGreatestFee/index.tsx | 26 +- .../src/hooks/useProxySupportedPapi/index.tsx | 66 +++ .../hooks/useSubmitExtrinsicPapi/index.tsx | 281 +++++++++ .../src/hooks/useSubmitExtrinsicPapi/types.ts | 24 + yarn.lock | 539 +++++++----------- 7 files changed, 655 insertions(+), 336 deletions(-) create mode 100644 packages/app/src/hooks/useBatchCallPapi/index.tsx create mode 100644 packages/app/src/hooks/useProxySupportedPapi/index.tsx create mode 100644 packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx create mode 100644 packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts diff --git a/packages/app/package.json b/packages/app/package.json index 948933807..2e68e823b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -24,14 +24,16 @@ "@fortawesome/react-fontawesome": "^0.2.2", "@ledgerhq/hw-transport-webhid": "^6.29.2", "@polkadot-api/merkleize-metadata": "^1.1.4", + "@polkadot-api/signers-common": "^0.1.1", + "@polkadot-api/substrate-bindings": "^0.9.3", "@polkadot/api": "^14.3.1", "@polkadot/rpc-provider": "^14.3.1", "@polkawatch/ddp-client": "^2.0.20", "@substrate/connect": "^2.0.1", - "@w3ux/extension-assets": "^0.4.0", + "@w3ux/extension-assets": "^1.0.0-beta.1", "@w3ux/factories": "^1.0.0", "@w3ux/hooks": "^1.3.1-beta.7", - "@w3ux/react-connect-kit": "^1.8.0", + "@w3ux/react-connect-kit": "2.0.0-beta.2", "@w3ux/react-odometer": "^1.1.0", "@w3ux/react-polkicon": "^2.0.1-alpha.0", "@w3ux/utils": "^1.1.1-beta.11", diff --git a/packages/app/src/hooks/useBatchCallPapi/index.tsx b/packages/app/src/hooks/useBatchCallPapi/index.tsx new file mode 100644 index 000000000..131e35f11 --- /dev/null +++ b/packages/app/src/hooks/useBatchCallPapi/index.tsx @@ -0,0 +1,49 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { AnyApi, MaybeAddress } from 'types'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useProxySupportedPapi } from 'hooks/useProxySupportedPapi'; +import type { UnsafeTx } from 'hooks/useSubmitExtrinsicPapi/types'; + +export const useBatchCallPapi = () => { + const { network } = useNetwork(); + const { activeProxy } = useActiveAccounts(); + const { isProxySupported } = useProxySupportedPapi(); + + const newBatchCall = (txs: UnsafeTx[], from: MaybeAddress): AnyApi => { + const { pApi } = ApiController.get(network); + + if (!pApi) { + return undefined; + } + + from = from || ''; + const batchTx = pApi.tx.Utility.batch({ + calls: txs.map((tx) => tx.decodedCall), + }); + + if (activeProxy && isProxySupported(batchTx, from)) { + return pApi.tx.Utility.batch({ + calls: txs.map( + (tx) => + pApi.tx.Proxy.proxy({ + real: { + type: 'Id', + value: from, + }, + force_proxy_type: null, + call: tx.decodedCall, + }).decodedCall + ), + }); + } + return batchTx; + }; + + return { + newBatchCall, + }; +}; diff --git a/packages/app/src/hooks/useBondGreatestFee/index.tsx b/packages/app/src/hooks/useBondGreatestFee/index.tsx index e058d3d2b..651cc451a 100644 --- a/packages/app/src/hooks/useBondGreatestFee/index.tsx +++ b/packages/app/src/hooks/useBondGreatestFee/index.tsx @@ -3,17 +3,14 @@ import BigNumber from 'bignumber.js'; import { useEffect, useMemo, useState } from 'react'; -import { useApi } from 'contexts/Api'; import { useTransferOptions } from 'contexts/TransferOptions'; import type { BondFor } from 'types'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; -interface Props { - bondFor: BondFor; -} - -export const useBondGreatestFee = ({ bondFor }: Props) => { - const { api } = useApi(); +export const useBondGreatestFee = ({ bondFor }: { bondFor: BondFor }) => { + const { network } = useNetwork(); const { activeAccount } = useActiveAccounts(); const { feeReserve, getTransferOptions } = useTransferOptions(); const transferOptions = useMemo( @@ -38,26 +35,29 @@ export const useBondGreatestFee = ({ bondFor }: Props) => { // estimate the largest possible tx fee based on users free balance. const txLargestFee = async () => { + const { pApi } = ApiController.get(network); + const bond = BigNumber.max( transferrableBalance.minus(feeReserve), 0 ).toString(); let tx = null; - if (!api) { + if (!pApi) { return new BigNumber(0); } if (bondFor === 'pool') { - tx = api.tx.nominationPools.bondExtra({ - FreeBalance: bond, + tx = pApi.tx.NominationPools.bond_extra({ + type: 'FreeBalance', + value: BigInt(bond), }); } else if (bondFor === 'nominator') { - tx = api.tx.staking.bondExtra(bond); + tx = pApi.tx.Staking.bond_extra({ bond }); } if (tx) { - const { partialFee } = await tx.paymentInfo(activeAccount || ''); - return new BigNumber(partialFee.toString()); + const { partial_fee } = await tx.getPaymentInfo(activeAccount || ''); + return new BigNumber(partial_fee.toString()); } return new BigNumber(0); }; diff --git a/packages/app/src/hooks/useProxySupportedPapi/index.tsx b/packages/app/src/hooks/useProxySupportedPapi/index.tsx new file mode 100644 index 000000000..0c3e12a6c --- /dev/null +++ b/packages/app/src/hooks/useProxySupportedPapi/index.tsx @@ -0,0 +1,66 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { + UnsupportedIfUniqueController, + isSupportedProxyCall, +} from 'config/proxies'; +import { useBonded } from 'contexts/Bonded'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useProxies } from 'contexts/Proxies'; +import type { MaybeAddress } from 'types'; +import type { AnyJson } from '@w3ux/types'; +import type { UnsafeTx } from 'hooks/useSubmitExtrinsicPapi/types'; + +export const useProxySupportedPapi = () => { + const { getBondedAccount } = useBonded(); + const { getProxyDelegate } = useProxies(); + const { activeProxy } = useActiveAccounts(); + + // If call is from controller, & controller is different from stash, then proxy is not + // supported. + const controllerNotSupported = (c: string, f: MaybeAddress) => + UnsupportedIfUniqueController.includes(c) && getBondedAccount(f) !== f; + + // Determine whether the provided tx is proxy supported. + const isProxySupported = (tx: UnsafeTx, delegator: MaybeAddress) => { + // if already wrapped, return. + if ( + tx?.decodedCall.type === 'Proxy' && + tx?.decodedCall.value.type === 'proxy' + ) { + return true; + } + + const proxyDelegate = getProxyDelegate(delegator, activeProxy); + const proxyType = proxyDelegate?.proxyType || ''; + const pallet: string = (tx?.decodedCall.type || '').toLowerCase(); + const method: string = (tx?.decodedCall.value.type || '').toLowerCase(); + const call = `${pallet}.${method}`; + + // If a batch call, test if every inner call is a supported proxy call. + if (call === 'utility.batch') { + return (tx?.decodedCall.value?.value?.calls || []) + .map((c: AnyJson) => ({ + pallet: c.type, + method: c.value.type, + })) + .every( + (c: AnyJson) => + (isSupportedProxyCall(proxyType, c.pallet, c.method) || + (c.pallet === 'Proxy' && c.method === 'proxy')) && + !controllerNotSupported(`${pallet}.${method}`, delegator) + ); + } + + // Check if the current call is a supported proxy call. + return ( + isSupportedProxyCall(proxyType, pallet, method) && + !controllerNotSupported(call, delegator) + ); + }; + + return { + isProxySupported, + }; +}; diff --git a/packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx b/packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx new file mode 100644 index 000000000..517f9c062 --- /dev/null +++ b/packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx @@ -0,0 +1,281 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import BigNumber from 'bignumber.js'; +import { useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { DappName, ManualSigners } from 'consts'; +import { useLedgerHardware } from 'contexts/LedgerHardware'; +import { useTxMeta } from 'contexts/TxMeta'; +import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; +import type { + UnsafeTx, + UseSubmitExtrinsic, + UseSubmitExtrinsicProps, +} from './types'; +import { NotificationsController } from 'controllers/Notifications'; +import { useExtensions } from '@w3ux/react-connect-kit'; +import { useProxySupportedPapi } from 'hooks/useProxySupportedPapi'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useBalances } from 'contexts/Balances'; +import { InvalidTxError } from 'polkadot-api'; +import { connectInjectedExtension } from 'polkadot-api/pjs-signer'; +import { formatAccountSs58 } from '@w3ux/utils'; + +export const useSubmitExtrinsicPapi = ({ + tx, + from, + shouldSubmit, + callbackSubmit, + callbackInBlock, +}: UseSubmitExtrinsicProps): UseSubmitExtrinsic => { + const { t } = useTranslation('library'); + const { network } = useNetwork(); + const { getNonce } = useBalances(); + const { activeProxy } = useActiveAccounts(); + const { extensionsStatus } = useExtensions(); + const { isProxySupported } = useProxySupportedPapi(); + const { handleResetLedgerTask } = useLedgerHardware(); + const { addPendingNonce, removePendingNonce } = useTxMeta(); + const { getAccount, requiresManualSign } = useImportedAccounts(); + const { txFees, setTxFees, setSender, incrementPayloadUid } = useTxMeta(); + + // Store given tx as a ref. + const txRef = useRef(tx); + + // Store given submit address as a ref. + const fromRef = useRef(from || ''); + + // Store whether the transaction is in progress. + const [submitting, setSubmitting] = useState(false); + + // Store the uid of the extrinsic. + const [uid] = useState(incrementPayloadUid()); + + // Track for one-shot transaction reset after submission. + const didTxReset = useRef(false); + + // If proxy account is active, wrap tx in a proxy call and set the sender to the proxy account. + const wrapTxIfActiveProxy = () => { + const { pApi } = ApiController.get(network); + + // if already wrapped, update fromRef and return. + if ( + txRef.current?.decodedCall.type === 'Proxy' && + txRef.current?.decodedCall.value.type === 'proxy' + ) { + if (activeProxy) { + fromRef.current = activeProxy; + } + return; + } + + if ( + pApi && + activeProxy && + txRef.current && + isProxySupported(txRef.current, fromRef.current) + ) { + // update submit address to active proxy account. + fromRef.current = activeProxy; + + // Do not wrap batch transactions. Proxy calls should already be wrapping each tx within the + // batch via `useBatchCall`. + if ( + txRef.current?.decodedCall.type === 'Utility' && + txRef.current?.decodedCall.value.type === 'batch' + ) { + return; + } + + // Not a batch transaction: wrap tx in proxy call. + txRef.current = pApi.tx.Proxy.proxy({ + real: { + type: 'Id', + value: from, + }, + forceProxyType: null, + call: txRef.current.decodedCall, + }); + } + }; + + // Calculate the estimated tx fee of the transaction. + const calculateEstimatedFee = async () => { + if (txRef.current === null) { + return; + } + + // get payment info + const partialFee = (await txRef.current.getPaymentInfo(fromRef.current)) + .partial_fee; + const partialFeeBn = new BigNumber(partialFee.toString()); + + // give tx fees to global useTxMeta context + if (partialFeeBn.toString() !== txFees.toString()) { + setTxFees(partialFeeBn); + } + }; + + // Extrinsic submission handler. + const onSubmit = async () => { + const account = getAccount(fromRef.current); + if (account === null || submitting || !shouldSubmit) { + return; + } + + const nonce = String(getNonce(fromRef.current)); + const { source } = account; + const isManualSigner = ManualSigners.includes(source); + + // if `activeAccount` is imported from an extension, ensure it is enabled. + if (!isManualSigner) { + const isInstalled = Object.entries(extensionsStatus).find( + ([id, status]) => id === source && status === 'connected' + ); + + if (!isInstalled) { + throw new Error(`${t('walletNotFound')}`); + } + + if (!window?.injectedWeb3?.[source]) { + throw new Error(`${t('walletNotFound')}`); + } + + // summons extension popup if not already connected. + window.injectedWeb3[source].enable(DappName); + } + + const onReady = () => { + addPendingNonce(nonce); + NotificationsController.emit({ + title: t('pending'), + subtitle: t('transactionInitiated'), + }); + if (callbackSubmit && typeof callbackSubmit === 'function') { + callbackSubmit(); + } + }; + + const onInBlock = () => { + setSubmitting(false); + removePendingNonce(nonce); + NotificationsController.emit({ + title: t('inBlock'), + subtitle: t('transactionInBlock'), + }); + if (callbackInBlock && typeof callbackInBlock === 'function') { + callbackInBlock(); + } + }; + + const onFinalizedEvent = () => { + NotificationsController.emit({ + title: t('finalized'), + subtitle: t('transactionSuccessful'), + }); + setSubmitting(false); + removePendingNonce(nonce); + }; + + const onFailedTx = () => { + NotificationsController.emit({ + title: t('failed'), + subtitle: t('errorWithTransaction'), + }); + setSubmitting(false); + removePendingNonce(nonce); + }; + + const resetTx = () => { + setSubmitting(false); + }; + + const onError = (type?: string) => { + resetTx(); + if (type === 'ledger') { + handleResetLedgerTask(); + } + removePendingNonce(nonce); + NotificationsController.emit({ + title: t('cancelled'), + subtitle: t('transactionCancelled'), + }); + }; + + const handleStatus = (status: string) => { + if (status === 'broadcasted') { + onReady(); + } + if (status === 'txBestBlocksState') { + onInBlock(); + } + }; + + // pre-submission state update + setSubmitting(true); + + // handle signed transaction. + let signer; + if (requiresManualSign(fromRef.current)) { + // TODO: Get custom signer here. + } else { + // Get the polkadot signer for this account. + signer = (await connectInjectedExtension(source)) + .getAccounts() + .find( + (a) => a.address === formatAccountSs58(fromRef.current, 42) + )?.polkadotSigner; + } + + try { + const sub = tx.signSubmitAndWatch(signer).subscribe({ + next: (result: { type: string }) => { + const eventType = result?.type; + + if (!didTxReset.current) { + didTxReset.current = true; + resetTx(); + } + handleStatus(eventType); + if (eventType === 'finalized') { + onFinalizedEvent(); + sub?.unsubscribe(); + } + }, + error: (err: Error) => { + if (err instanceof InvalidTxError) { + onFailedTx(); + } + sub?.unsubscribe(); + }, + }); + } catch (e) { + onError('default'); + } + }; + + // Refresh state upon `tx` updates. + useEffect(() => { + // update txRef to latest tx. + txRef.current = tx; + // update submit address to latest from. + fromRef.current = from || ''; + // wrap tx in proxy call if active proxy & proxy supported. + wrapTxIfActiveProxy(); + // ensure sender is up to date. + setSender(fromRef.current); + // re-calculate estimated tx fee. + calculateEstimatedFee(); + }, [tx?.toString(), tx?.method?.args?.calls?.toString(), from]); + + return { + uid, + onSubmit, + submitting, + submitAddress: fromRef.current, + proxySupported: isProxySupported(txRef.current, fromRef.current), + }; +}; diff --git a/packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts b/packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts new file mode 100644 index 000000000..292c83817 --- /dev/null +++ b/packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts @@ -0,0 +1,24 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { UnsafeTransaction } from 'polkadot-api'; +import type { AnyApi, MaybeAddress } from 'types'; + +export interface UseSubmitExtrinsicProps { + tx: AnyApi; + from: MaybeAddress; + shouldSubmit: boolean; + callbackSubmit?: () => void; + callbackInBlock?: () => void; +} + +export interface UseSubmitExtrinsic { + uid: number; + onSubmit: () => void; + submitting: boolean; + proxySupported: boolean; + submitAddress: MaybeAddress; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type UnsafeTx = UnsafeTransaction; diff --git a/yarn.lock b/yarn.lock index 846ee4ff5..237b8fc47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -236,17 +236,6 @@ __metadata: languageName: node linkType: hard -"@chainsafe/metamask-polkadot-adapter@npm:^0.6.0": - version: 0.6.0 - resolution: "@chainsafe/metamask-polkadot-adapter@npm:0.6.0" - dependencies: - "@polkadot/api": "npm:^10.9.1" - "@polkadot/extension-inject": "npm:^0.46.5" - "@polkadot/types-augment": "npm:^10.9.1" - checksum: 10c0/a9cdfdd175dc6d9f6aa1d6f4515185d3f2b4089154dd0cd47749f66c6e5965cd7ff0187245763f7aafa1be49d41e1d2e8d721c78a0e831afc506fb6ff229ae15 - languageName: node - linkType: hard - "@coinbase/wallet-sdk@npm:4.2.3": version: 4.2.3 resolution: "@coinbase/wallet-sdk@npm:4.2.3" @@ -1808,20 +1797,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - dependencies: - "@polkadot-api/metadata-builders": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/substrate-bindings": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/substrate-client": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/utils": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - peerDependencies: - rxjs: ">=7.8.0" - checksum: 10c0/f80744e1612446a09fdd3f5a98cdf8f9a5b9fdeddcfa083084b49e0ae63e4b4875a326487258bcbb60e6edc2fbb831436abe4a48b87942c4417ba1ee05e95648 - languageName: node - linkType: hard - "@polkadot-api/codegen@npm:0.12.7": version: 0.12.7 resolution: "@polkadot-api/codegen@npm:0.12.7" @@ -1859,13 +1834,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/63160c2227996e1d8926a7d0281cd7a747e53e8049a63c74087425bca6a986662f46ca8e0be5b31882816199aeef1863c8ee9a7d2834d2ee1a0d1bc6dc7d61f3 - 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" @@ -1887,13 +1855,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/json-rpc-provider@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/json-rpc-provider@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/2cc039dfd0234696e624a48d94f07b68ab3b1fc402f703b142b05f640301c47a3c5315a6ef10d2a61c31d6f93181d1240ba5b4e169e5cb8a428f5eb9613b0123 - 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" @@ -1928,16 +1889,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/metadata-builders@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/metadata-builders@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - dependencies: - "@polkadot-api/substrate-bindings": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/utils": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/15dcba05a0c7cafaab72edc6c6246133cc25f94a158f0ac2091e3c774271cae6cf64658cefe5db9d57eb16e9a9cc8936f387547064237d129d7675e9ca27e1d3 - languageName: node - linkType: hard - "@polkadot-api/metadata-builders@npm:0.3.2": version: 0.3.2 resolution: "@polkadot-api/metadata-builders@npm:0.3.2" @@ -2038,7 +1989,7 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/signers-common@npm:0.1.1": +"@polkadot-api/signers-common@npm:0.1.1, @polkadot-api/signers-common@npm:^0.1.1": version: 0.1.1 resolution: "@polkadot-api/signers-common@npm:0.1.1" dependencies: @@ -2072,18 +2023,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/substrate-bindings@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/substrate-bindings@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - dependencies: - "@noble/hashes": "npm:^1.3.1" - "@polkadot-api/utils": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@scure/base": "npm:^1.1.1" - scale-ts: "npm:^1.6.0" - checksum: 10c0/f6273cbb1ed74f253f83dfb9e4b46375eb7430e17e69a2f3d631ff80d8ff7b7e871cdf89639ef18635a659a60623e384c894ab2e1889d70442f7ca73c05b44e3 - languageName: node - linkType: hard - "@polkadot-api/substrate-bindings@npm:0.6.0": version: 0.6.0 resolution: "@polkadot-api/substrate-bindings@npm:0.6.0" @@ -2108,13 +2047,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/substrate-client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/substrate-client@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/56dc28993d06a14b4091f028956f8bb179d3c602a3e3979a978eb2192233ce6ad2f12d4a583e859480521d4c75629aab1d7f0effd989da0ebb90a1b51e7ee073 - languageName: node - linkType: hard - "@polkadot-api/substrate-client@npm:0.3.0": version: 0.3.0 resolution: "@polkadot-api/substrate-client@npm:0.3.0" @@ -2135,13 +2067,6 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/utils@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0": - version: 0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0 - resolution: "@polkadot-api/utils@npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - checksum: 10c0/63c36ac45d63a4f8718d1324be0290dadfbfbb387e440d5962de13200861400a6af09a5e96b73f985fd4c27d03c93927420c8145e49e1e822653a3c223dda645 - languageName: node - linkType: hard - "@polkadot-api/utils@npm:0.1.0": version: 0.1.0 resolution: "@polkadot-api/utils@npm:0.1.0" @@ -2174,18 +2099,18 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-augment@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/api-augment@npm:10.13.1" +"@polkadot/api-augment@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/api-augment@npm:12.4.2" dependencies: - "@polkadot/api-base": "npm:10.13.1" - "@polkadot/rpc-augment": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-augment": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/5f1faa67bc8a574fe97debc8aa7e7f6795aa437a1212121188d6bbeb1d465b47b0a1a22b8268f3136e1956dcdf0c1f9004b2a7968a275414e93b0a4e91ba7edc + "@polkadot/api-base": "npm:12.4.2" + "@polkadot/rpc-augment": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-augment": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/4cab0d50cde43eb25d830fca93cdfe10198302d44a271aacfa274d171671ce4fa4b07bafd0886f8cf78ce10478762e337e9fe27a652080a1dc6f5823be05bcb3 languageName: node linkType: hard @@ -2204,16 +2129,16 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-base@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/api-base@npm:10.13.1" +"@polkadot/api-base@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/api-base@npm:12.4.2" dependencies: - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/3adaa5d3c34e16cc28a0427839c87aab7b1d022c8b6992c43dc91ab7e910d0c8e17dca9ee6b7c9e27a6486aa8878dd7deae79870d7d44ede92aba8421241c249 + tslib: "npm:^2.6.3" + checksum: 10c0/b2e7b7a3f93070b09f5992f8128a378f47a8c808a08a2890c50abcaded040ab30e8b1b5c2b693c375f2187032dc8d010d407ac14e1e39fcb984a2e0f9f05e23d languageName: node linkType: hard @@ -2230,21 +2155,21 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-derive@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/api-derive@npm:10.13.1" +"@polkadot/api-derive@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/api-derive@npm:12.4.2" dependencies: - "@polkadot/api": "npm:10.13.1" - "@polkadot/api-augment": "npm:10.13.1" - "@polkadot/api-base": "npm:10.13.1" - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - "@polkadot/util-crypto": "npm:^12.6.2" + "@polkadot/api": "npm:12.4.2" + "@polkadot/api-augment": "npm:12.4.2" + "@polkadot/api-base": "npm:12.4.2" + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/91df2f399b0133bdcc19edb595e058481b1ffc63e80dbbc58c928eb04982cae1372fe7e3eda6b05888c88b68ae152f46646d080f4b940a5cca9307a681b7c78f + tslib: "npm:^2.6.3" + checksum: 10c0/47f0698fa2b3f1adc03af93fbf7c533d8801bad52c7d8989ab019ce215bbd1c1e8d7a0c18d89839e86619ae9e93385cb7963feecc323dad79751de1496d0c23c languageName: node linkType: hard @@ -2266,28 +2191,28 @@ __metadata: languageName: node linkType: hard -"@polkadot/api@npm:10.13.1, @polkadot/api@npm:^10.12.4, @polkadot/api@npm:^10.9.1": - version: 10.13.1 - resolution: "@polkadot/api@npm:10.13.1" - dependencies: - "@polkadot/api-augment": "npm:10.13.1" - "@polkadot/api-base": "npm:10.13.1" - "@polkadot/api-derive": "npm:10.13.1" - "@polkadot/keyring": "npm:^12.6.2" - "@polkadot/rpc-augment": "npm:10.13.1" - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/rpc-provider": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-augment": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/types-create": "npm:10.13.1" - "@polkadot/types-known": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - "@polkadot/util-crypto": "npm:^12.6.2" +"@polkadot/api@npm:12.4.2, @polkadot/api@npm:^12.0.2": + version: 12.4.2 + resolution: "@polkadot/api@npm:12.4.2" + dependencies: + "@polkadot/api-augment": "npm:12.4.2" + "@polkadot/api-base": "npm:12.4.2" + "@polkadot/api-derive": "npm:12.4.2" + "@polkadot/keyring": "npm:^13.0.2" + "@polkadot/rpc-augment": "npm:12.4.2" + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/rpc-provider": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-augment": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/types-create": "npm:12.4.2" + "@polkadot/types-known": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" eventemitter3: "npm:^5.0.1" rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/c2192c51aca790b2d8915a08e55e3b0461c49a1ce3ceccc5597cd74d68367ff0a881a0a864a0897835364482618d0a945247576573ef7c00688f1a25a5347ccc + tslib: "npm:^2.6.3" + checksum: 10c0/64e9713508193a971e7fe830d39c625101b9dc9ce987ee38118d9a2e7e913eaeb9231cd7776b4d00ac293b23052c12ca6e7867fddacb8f7ec283017ee68ad1aa languageName: node linkType: hard @@ -2316,13 +2241,13 @@ __metadata: languageName: node linkType: hard -"@polkadot/extension-inject@npm:0.46.9, @polkadot/extension-inject@npm:^0.46.5": - version: 0.46.9 - resolution: "@polkadot/extension-inject@npm:0.46.9" +"@polkadot/extension-inject@npm:0.48.2": + version: 0.48.2 + resolution: "@polkadot/extension-inject@npm:0.48.2" dependencies: - "@polkadot/api": "npm:^10.12.4" - "@polkadot/rpc-provider": "npm:^10.12.4" - "@polkadot/types": "npm:^10.12.4" + "@polkadot/api": "npm:^12.0.2" + "@polkadot/rpc-provider": "npm:^12.0.2" + "@polkadot/types": "npm:^12.0.2" "@polkadot/util": "npm:^12.6.2" "@polkadot/util-crypto": "npm:^12.6.2" "@polkadot/x-global": "npm:^12.6.2" @@ -2330,7 +2255,7 @@ __metadata: peerDependencies: "@polkadot/api": "*" "@polkadot/util": "*" - checksum: 10c0/6afd3f8f5b1b803004eb50ab4588035c679933533042010cf55f685d21d8f34e2d3c8644f61831098b0cbd1abe8a669b48c22a1d19d0cc06175e9ff798e9a87c + checksum: 10c0/c8281942bf30f31c9b5388475483197e023a113da839b9144d8c558124728e7edd48b305e9f2d7f9674a3f0b6517571b6993eb0f4d40157a796bd772094e7ca3 languageName: node linkType: hard @@ -2348,7 +2273,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/keyring@npm:^13.2.3": +"@polkadot/keyring@npm:^13.0.2, @polkadot/keyring@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/keyring@npm:13.2.3" dependencies: @@ -2362,7 +2287,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/networks@npm:12.6.2, @polkadot/networks@npm:^12.6.2": +"@polkadot/networks@npm:12.6.2": version: 12.6.2 resolution: "@polkadot/networks@npm:12.6.2" dependencies: @@ -2373,7 +2298,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/networks@npm:13.2.3, @polkadot/networks@npm:^13.2.3": +"@polkadot/networks@npm:13.2.3, @polkadot/networks@npm:^13.0.2, @polkadot/networks@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/networks@npm:13.2.3" dependencies: @@ -2384,16 +2309,16 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-augment@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/rpc-augment@npm:10.13.1" +"@polkadot/rpc-augment@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/rpc-augment@npm:12.4.2" dependencies: - "@polkadot/rpc-core": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/5389ac83712cb0144e5f6b4319f76a54e8c85d455043ba688d32b233bc83202479f5506a8c8a0a7b0e8688150ca4c8d63ef57b2255e52827a5738eb600ab01ee + "@polkadot/rpc-core": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/8e078db8496337c16bfb474cb557aaed5cccb2c1a3b8a56ad729fea308b23745c0cf5db10212f5653b60344add2084fc5ac7521a7b08c19fd309280e539336cf languageName: node linkType: hard @@ -2410,17 +2335,17 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-core@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/rpc-core@npm:10.13.1" +"@polkadot/rpc-core@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/rpc-core@npm:12.4.2" dependencies: - "@polkadot/rpc-augment": "npm:10.13.1" - "@polkadot/rpc-provider": "npm:10.13.1" - "@polkadot/types": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" + "@polkadot/rpc-augment": "npm:12.4.2" + "@polkadot/rpc-provider": "npm:12.4.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" rxjs: "npm:^7.8.1" - tslib: "npm:^2.6.2" - checksum: 10c0/423479b6332eae4729076e1faa7371e7516021348ec6b4a4323a25740ea2577afdb395e767580babd854da14f63065cd4ac766a58b61263d09db64f1f6bb2599 + tslib: "npm:^2.6.3" + checksum: 10c0/4201b1d503801a672f2ceb0e6ab90226eb03c2d668879669656d73a952c556ba32e0a85c479d87ccd0aa80ce8fbc69ddde69abee462517dabc4736b3993deca6 languageName: node linkType: hard @@ -2438,27 +2363,27 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-provider@npm:10.13.1, @polkadot/rpc-provider@npm:^10.12.4": - version: 10.13.1 - resolution: "@polkadot/rpc-provider@npm:10.13.1" +"@polkadot/rpc-provider@npm:12.4.2, @polkadot/rpc-provider@npm:^12.0.2": + version: 12.4.2 + resolution: "@polkadot/rpc-provider@npm:12.4.2" dependencies: - "@polkadot/keyring": "npm:^12.6.2" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-support": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - "@polkadot/util-crypto": "npm:^12.6.2" - "@polkadot/x-fetch": "npm:^12.6.2" - "@polkadot/x-global": "npm:^12.6.2" - "@polkadot/x-ws": "npm:^12.6.2" - "@substrate/connect": "npm:0.8.8" + "@polkadot/keyring": "npm:^13.0.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-support": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" + "@polkadot/x-fetch": "npm:^13.0.2" + "@polkadot/x-global": "npm:^13.0.2" + "@polkadot/x-ws": "npm:^13.0.2" + "@substrate/connect": "npm:0.8.11" eventemitter3: "npm:^5.0.1" mock-socket: "npm:^9.3.1" - nock: "npm:^13.5.0" - tslib: "npm:^2.6.2" + nock: "npm:^13.5.4" + tslib: "npm:^2.6.3" dependenciesMeta: "@substrate/connect": optional: true - checksum: 10c0/13bc88d973a55b131bea9429831caa19b85cde73bb0c8d124bbeb1f9b5cd63d4e297a38be477e75b8930e181ab837249fec06417dc52cc5c438149313e5a2947 + checksum: 10c0/59968dfae8ecaed840ec61c84d50953faf14d76dbcfcf61ef4acb88c9f4ef07c0c2b9c5227cb2b63801a0895d3f10edb686ae3126269eda5635f29796fa20fdf languageName: node linkType: hard @@ -2486,15 +2411,27 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-augment@npm:10.13.1, @polkadot/types-augment@npm:^10.9.1": - version: 10.13.1 - resolution: "@polkadot/types-augment@npm:10.13.1" +"@polkadot/types-augment@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types-augment@npm:12.1.1" dependencies: - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" + "@polkadot/types": "npm:12.1.1" + "@polkadot/types-codec": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" tslib: "npm:^2.6.2" - checksum: 10c0/0686a834fd5d4db1cc74c184057cf1c47f008d3d541866cb2f2a38464c6a41cb159e5ec914b2e3d68511f6c6c7798238b58ec3bd1315a67fbb1ee073147457c6 + checksum: 10c0/20342a217823e039f3c0cc011319429345b98472230472e3ec3563d97581ad2a20a974cdaf66c8ce2d49381a5cdf759326027b6f55bef18b396decfd0db37030 + languageName: node + linkType: hard + +"@polkadot/types-augment@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-augment@npm:12.4.2" + dependencies: + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/9dcae5ec9fd7aaac9d3ffe2f5adb9b5c4704376018db4860215ca38805b189c5ef2f90360da0ff29d8b9a9715617bb5eabf6870bcfd8f9eeba974d6eb9b5bfab languageName: node linkType: hard @@ -2510,14 +2447,25 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-codec@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-codec@npm:10.13.1" +"@polkadot/types-codec@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types-codec@npm:12.1.1" dependencies: "@polkadot/util": "npm:^12.6.2" "@polkadot/x-bigint": "npm:^12.6.2" tslib: "npm:^2.6.2" - checksum: 10c0/8a35c492006502804a5531231c14037ab98a13f345f4e550142254e69d62d451f0caa89347ac689b92f90b582fe6ab2f1c8eca30cdf327951323b6400fca2e50 + checksum: 10c0/d717a4ad746c5748c683fe449814a85e0c5f4b2530c2448f5950918c83f43370bdedd51ea9c5f31da8992f674c05324649f2e1d350d5b688316be345e2257893 + languageName: node + linkType: hard + +"@polkadot/types-codec@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-codec@npm:12.4.2" + dependencies: + "@polkadot/util": "npm:^13.0.2" + "@polkadot/x-bigint": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/2a56694e6998fc2afbe4fe8a9f9805eb251e880f1343af380f70c42965d30bae2249e5a5f346e675d5f78173770ebd4fa0758ed8b9f1397db8183eb686d11842 languageName: node linkType: hard @@ -2532,14 +2480,25 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-create@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-create@npm:10.13.1" +"@polkadot/types-create@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types-create@npm:12.1.1" dependencies: - "@polkadot/types-codec": "npm:10.13.1" + "@polkadot/types-codec": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" tslib: "npm:^2.6.2" - checksum: 10c0/efe57d84f6088111b53d29db07ab9bf951f79c3e9b4875882c7a9bb0a6f1e00230e63a84cf2a850528119bbfa7f30bdfb21bba645e3922d88ff83092ea0350c5 + checksum: 10c0/b4e3e4f93626f816a104868467b5299a19416b14b6783b576c498636d46a7d86488d623a91d5b3f106e4f7717959c90e1b9d0a65f2a60893ecb4f491ea5c8b54 + languageName: node + linkType: hard + +"@polkadot/types-create@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-create@npm:12.4.2" + dependencies: + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/c075d07c2d3212f0ab9772cd008bfadccde7a35f6366c6704a326f8e5199fce7e7eb7959a6bd229b69fcbc3900c522892f94b08b4cd991be6e42f2a786585d0f languageName: node linkType: hard @@ -2554,17 +2513,17 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-known@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-known@npm:10.13.1" +"@polkadot/types-known@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-known@npm:12.4.2" dependencies: - "@polkadot/networks": "npm:^12.6.2" - "@polkadot/types": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/types-create": "npm:10.13.1" - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/ce4e5b402e4d923a1b93475997e57bb6b7f95cc679a466c7d563d9d033872479100cf5ac72c58faaf57610602c913555ef0b4bd4672c6c76fc733c062d41959b + "@polkadot/networks": "npm:^13.0.2" + "@polkadot/types": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/types-create": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/2e7fdd895c6478e102b91d7cb3428b75c34aa0b037ba9d2d2b97d350a19ed7bd6ea4f2c91c52eef4039ebec330fd1cd58a0f88fc30882c2a6623c0cae78b421a languageName: node linkType: hard @@ -2582,13 +2541,13 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-support@npm:10.13.1": - version: 10.13.1 - resolution: "@polkadot/types-support@npm:10.13.1" +"@polkadot/types-support@npm:12.4.2": + version: 12.4.2 + resolution: "@polkadot/types-support@npm:12.4.2" dependencies: - "@polkadot/util": "npm:^12.6.2" - tslib: "npm:^2.6.2" - checksum: 10c0/a9bf65b139b1c861acc198ce650e14d2014f88d30f890710baf3481caa16b44dc39122b05d34cc86211b08e082cf4e7d5680ba3a4008711fe86b70d62a65219c + "@polkadot/util": "npm:^13.0.2" + tslib: "npm:^2.6.3" + checksum: 10c0/0277fe88cac0f2447b3655bb5ca32dfe7dc7e35a82d22873baf95a0272a207f73853e94b3340e7e9230945dfaa8f14f93f4ffb13c8b29d449f602e8c5fe3f3c2 languageName: node linkType: hard @@ -2602,19 +2561,35 @@ __metadata: languageName: node linkType: hard -"@polkadot/types@npm:10.13.1, @polkadot/types@npm:^10.11.2, @polkadot/types@npm:^10.12.4": - version: 10.13.1 - resolution: "@polkadot/types@npm:10.13.1" +"@polkadot/types@npm:12.1.1": + version: 12.1.1 + resolution: "@polkadot/types@npm:12.1.1" dependencies: "@polkadot/keyring": "npm:^12.6.2" - "@polkadot/types-augment": "npm:10.13.1" - "@polkadot/types-codec": "npm:10.13.1" - "@polkadot/types-create": "npm:10.13.1" + "@polkadot/types-augment": "npm:12.1.1" + "@polkadot/types-codec": "npm:12.1.1" + "@polkadot/types-create": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" "@polkadot/util-crypto": "npm:^12.6.2" rxjs: "npm:^7.8.1" tslib: "npm:^2.6.2" - checksum: 10c0/1fe50d7ba10368dd944fec0e2da740f8c665c1c417362ab5ed1587d9083259c65064e04a862443d1a6c9f1da86b8dee6a4945e711064f0318895a38ad1303e3b + checksum: 10c0/46395b18dc78d7d8295df6a80fc8a00d73d942ffa2ef8bd7fc6591ac3d018f70c0765ac54b848c508514196c7bb302fc08ac80779ca1431f57c423b20c7080ac + languageName: node + linkType: hard + +"@polkadot/types@npm:12.4.2, @polkadot/types@npm:^12.0.2": + version: 12.4.2 + resolution: "@polkadot/types@npm:12.4.2" + dependencies: + "@polkadot/keyring": "npm:^13.0.2" + "@polkadot/types-augment": "npm:12.4.2" + "@polkadot/types-codec": "npm:12.4.2" + "@polkadot/types-create": "npm:12.4.2" + "@polkadot/util": "npm:^13.0.2" + "@polkadot/util-crypto": "npm:^13.0.2" + rxjs: "npm:^7.8.1" + tslib: "npm:^2.6.3" + checksum: 10c0/bdea1658a367678a158d5d6ba6224a081cfd5fb38b6d56c360321e40628a23261261c869e7ab1ac0c89c0140777f532963c46999e5fb0f13233599a32eabdf99 languageName: node linkType: hard @@ -2654,7 +2629,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/util-crypto@npm:13.2.3, @polkadot/util-crypto@npm:^13.1.1, @polkadot/util-crypto@npm:^13.2.3": +"@polkadot/util-crypto@npm:13.2.3, @polkadot/util-crypto@npm:^13.0.2, @polkadot/util-crypto@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/util-crypto@npm:13.2.3" dependencies: @@ -2689,7 +2664,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/util@npm:13.2.3, @polkadot/util@npm:^13.1.1, @polkadot/util@npm:^13.2.3": +"@polkadot/util@npm:13.2.3, @polkadot/util@npm:^13.0.2, @polkadot/util@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/util@npm:13.2.3" dependencies: @@ -2794,7 +2769,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-bigint@npm:13.2.3, @polkadot/x-bigint@npm:^13.2.3": +"@polkadot/x-bigint@npm:13.2.3, @polkadot/x-bigint@npm:^13.0.2, @polkadot/x-bigint@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/x-bigint@npm:13.2.3" dependencies: @@ -2804,18 +2779,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-fetch@npm:^12.6.2": - version: 12.6.2 - resolution: "@polkadot/x-fetch@npm:12.6.2" - dependencies: - "@polkadot/x-global": "npm:12.6.2" - node-fetch: "npm:^3.3.2" - tslib: "npm:^2.6.2" - checksum: 10c0/c4e34c28f4374db3b6795b31f63434b4241896a82cd1a0aa81196c7dbe8aa345069a39d27d5c3af214d8d2824154c6fe1fcbe9cb22af32f9a2c3fd22dc4b8583 - languageName: node - linkType: hard - -"@polkadot/x-fetch@npm:^13.2.3": +"@polkadot/x-fetch@npm:^13.0.2, @polkadot/x-fetch@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/x-fetch@npm:13.2.3" dependencies: @@ -2835,7 +2799,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-global@npm:13.2.3, @polkadot/x-global@npm:^13.2.3": +"@polkadot/x-global@npm:13.2.3, @polkadot/x-global@npm:^13.0.2, @polkadot/x-global@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/x-global@npm:13.2.3" dependencies: @@ -2910,18 +2874,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-ws@npm:^12.6.2": - version: 12.6.2 - resolution: "@polkadot/x-ws@npm:12.6.2" - dependencies: - "@polkadot/x-global": "npm:12.6.2" - tslib: "npm:^2.6.2" - ws: "npm:^8.15.1" - checksum: 10c0/15565803a34aa7d6654c4c05725f5f44e504caa69f590523c5569fcbd66cf1e467de03e3e13a4d71bb60efceb28c60fd5719bee5efd721c020cf470025bbeb29 - languageName: node - linkType: hard - -"@polkadot/x-ws@npm:^13.2.3": +"@polkadot/x-ws@npm:^13.0.2, @polkadot/x-ws@npm:^13.2.3": version: 13.2.3 resolution: "@polkadot/x-ws@npm:13.2.3" dependencies: @@ -2932,12 +2885,13 @@ __metadata: languageName: node linkType: hard -"@polkagate/extension-dapp@npm:^0.46.13": - version: 0.46.13 - resolution: "@polkagate/extension-dapp@npm:0.46.13" +"@polkagate/extension-dapp@npm:^0.48.2": + version: 0.48.2 + resolution: "@polkagate/extension-dapp@npm:0.48.2" dependencies: - "@polkadot/extension-inject": "npm:0.46.9" - "@polkadot/types": "npm:^10.11.2" + "@metamask/utils": "npm:^9.0.0" + "@polkadot/extension-inject": "npm:0.48.2" + "@polkadot/types": "npm:12.1.1" "@polkadot/util": "npm:^12.6.2" "@polkadot/util-crypto": "npm:^12.6.2" tslib: "npm:^2.6.2" @@ -2945,7 +2899,7 @@ __metadata: "@polkadot/api": "*" "@polkadot/util": "*" "@polkadot/util-crypto": "*" - checksum: 10c0/fecad5ba8ed04c7a745b5870c75d67636dd8bbe8230a809aad443d73dcac3ba4d0bc4ba164317564f1e1fcb425e47c0c2189d5ada583e39975ec64b47254fa17 + checksum: 10c0/87dc06ff885533135ee4fde2f74bb9809ce3ed140fe2e3baef999831537ae3c537e014140f9c8e49cdf32c1f912194b58970e42161cd9f4210dcbca5cedc35f1 languageName: node linkType: hard @@ -3448,7 +3402,7 @@ __metadata: languageName: node linkType: hard -"@substrate/connect-known-chains@npm:^1.1.1, @substrate/connect-known-chains@npm:^1.1.5, @substrate/connect-known-chains@npm:^1.7.0": +"@substrate/connect-known-chains@npm:^1.1.5, @substrate/connect-known-chains@npm:^1.7.0": version: 1.7.0 resolution: "@substrate/connect-known-chains@npm:1.7.0" checksum: 10c0/454f115eed8c2299b9ba66c8c3328af80767c501fb15a4e54e6788cc5d9ab6289c91a44a215dbaab8df4fda41063bafff5eee3926b43cd8787af86610e45c673 @@ -3467,18 +3421,6 @@ __metadata: languageName: node linkType: hard -"@substrate/connect@npm:0.8.8": - version: 0.8.8 - resolution: "@substrate/connect@npm:0.8.8" - dependencies: - "@substrate/connect-extension-protocol": "npm:^2.0.0" - "@substrate/connect-known-chains": "npm:^1.1.1" - "@substrate/light-client-extension-helpers": "npm:^0.0.4" - smoldot: "npm:2.0.22" - checksum: 10c0/d2507a5c2392523c31b1e25a4b0955b7c271406d527511c5ceab561fff0d8788262d638ff996dc2978464998f5db78cf373b34d2af86b040686c86f36093ec65 - languageName: node - linkType: hard - "@substrate/connect@npm:^2.0.1": version: 2.0.1 resolution: "@substrate/connect@npm:2.0.1" @@ -3498,23 +3440,6 @@ __metadata: languageName: node linkType: hard -"@substrate/light-client-extension-helpers@npm:^0.0.4": - version: 0.0.4 - resolution: "@substrate/light-client-extension-helpers@npm:0.0.4" - dependencies: - "@polkadot-api/client": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/json-rpc-provider": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/json-rpc-provider-proxy": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@polkadot-api/substrate-client": "npm:0.0.1-492c132563ea6b40ae1fc5470dec4cd18768d182.1.0" - "@substrate/connect-extension-protocol": "npm:^2.0.0" - "@substrate/connect-known-chains": "npm:^1.1.1" - rxjs: "npm:^7.8.1" - peerDependencies: - smoldot: 2.x - checksum: 10c0/51e44ab2f3efaf8482047d3ed89e51327b54f53bd9d4169ce3738daf22e0afa849560cfd1f487ae809ea9adfdb93812f1cb74329d44cf686c0b552493b47b122 - languageName: node - linkType: hard - "@substrate/light-client-extension-helpers@npm:^1.0.0": version: 1.0.0 resolution: "@substrate/light-client-extension-helpers@npm:1.0.0" @@ -4168,12 +4093,12 @@ __metadata: languageName: node linkType: hard -"@w3ux/extension-assets@npm:0.4.0, @w3ux/extension-assets@npm:^0.4.0": - version: 0.4.0 - resolution: "@w3ux/extension-assets@npm:0.4.0" +"@w3ux/extension-assets@npm:1.0.0-beta.1, @w3ux/extension-assets@npm:^1.0.0-beta.1": + version: 1.0.0-beta.1 + resolution: "@w3ux/extension-assets@npm:1.0.0-beta.1" peerDependencies: react: ^18 - checksum: 10c0/8cacff647d2d9c8deef43e83f57cfeb30a252cb716afb18c048b876012b9ce8f10e24d2a79f91e180014a46f9c9ca3346fd061fd6b9f98175881176bee90d4a2 + checksum: 10c0/bb9d43db351e333d7fa6b49977ef2ce7077bfe7d855dd0960f5e9e0e08d35bfe92b6431f35a74d3140b3fb4642296db7d96b188c80cce2e393c2c9c066244983 languageName: node linkType: hard @@ -4186,15 +4111,6 @@ __metadata: languageName: node linkType: hard -"@w3ux/hooks@npm:^1.1.0": - version: 1.2.1 - resolution: "@w3ux/hooks@npm:1.2.1" - peerDependencies: - react: ^18 - checksum: 10c0/542eae337e26c2202b53625ee7178bcf8fd748aac5b37c8851ef143251a09e37bf8a95916039661dcf76fe31004d3231c87d2dad6237251dc843351282173d72 - languageName: node - linkType: hard - "@w3ux/hooks@npm:^1.3.1-beta.7": version: 1.3.1-beta.7 resolution: "@w3ux/hooks@npm:1.3.1-beta.7" @@ -4207,18 +4123,17 @@ __metadata: languageName: node linkType: hard -"@w3ux/react-connect-kit@npm:^1.8.0": - version: 1.8.0 - resolution: "@w3ux/react-connect-kit@npm:1.8.0" +"@w3ux/react-connect-kit@npm:2.0.0-beta.2": + version: 2.0.0-beta.2 + resolution: "@w3ux/react-connect-kit@npm:2.0.0-beta.2" dependencies: - "@chainsafe/metamask-polkadot-adapter": "npm:^0.6.0" - "@polkagate/extension-dapp": "npm:^0.46.13" - "@w3ux/extension-assets": "npm:0.4.0" - "@w3ux/hooks": "npm:^1.1.0" - "@w3ux/utils": "npm:^0.9.0" + "@polkagate/extension-dapp": "npm:^0.48.2" + "@w3ux/extension-assets": "npm:1.0.0-beta.1" + "@w3ux/hooks": "npm:^1.3.1-beta.7" + "@w3ux/utils": "npm:^1.1.1-beta.11" peerDependencies: "@polkadot/api": ^11 - checksum: 10c0/8023634fdcd2a2fd50e6774f47d10ccc6c83c608821228372958f5bf21317830ce12a805c0306a5a6594c9b19375c1b2603602a1779f7e1fdf8579940b8fc633 + checksum: 10c0/8db87ebe1a5ff9eac3a275e05043c8f920283b75edd193393ee27898708673aa034fd4ed7a71a1ffc915e477f1c1a33bb5159d023b8a2fdaa164cc9a8f573113 languageName: node linkType: hard @@ -4253,17 +4168,6 @@ __metadata: languageName: node linkType: hard -"@w3ux/utils@npm:^0.9.0": - version: 0.9.1 - resolution: "@w3ux/utils@npm:0.9.1" - dependencies: - "@polkadot/util": "npm:^13.1.1" - "@polkadot/util-crypto": "npm:^13.1.1" - bignumber.js: "npm:^9.1.1" - checksum: 10c0/624ca3fd80cd5a0ed16b28342b41d643cc1aac7dbe68e00f3c4a9e460da21ac28d09e86582aad7c642e5907069c4544705c3bc638d836625916694e21774861c - languageName: node - linkType: hard - "@w3ux/utils@npm:^1.1.1-beta.11": version: 1.1.1-beta.11 resolution: "@w3ux/utils@npm:1.1.1-beta.11" @@ -4878,14 +4782,16 @@ __metadata: "@fortawesome/react-fontawesome": "npm:^0.2.2" "@ledgerhq/hw-transport-webhid": "npm:^6.29.2" "@polkadot-api/merkleize-metadata": "npm:^1.1.4" + "@polkadot-api/signers-common": "npm:^0.1.1" + "@polkadot-api/substrate-bindings": "npm:^0.9.3" "@polkadot/api": "npm:^14.3.1" "@polkadot/rpc-provider": "npm:^14.3.1" "@polkawatch/ddp-client": "npm:^2.0.20" "@substrate/connect": "npm:^2.0.1" - "@w3ux/extension-assets": "npm:^0.4.0" + "@w3ux/extension-assets": "npm:^1.0.0-beta.1" "@w3ux/factories": "npm:^1.0.0" "@w3ux/hooks": "npm:^1.3.1-beta.7" - "@w3ux/react-connect-kit": "npm:^1.8.0" + "@w3ux/react-connect-kit": "npm:2.0.0-beta.2" "@w3ux/react-odometer": "npm:^1.1.0" "@w3ux/react-polkicon": "npm:^2.0.1-alpha.0" "@w3ux/utils": "npm:^1.1.1-beta.11" @@ -5128,7 +5034,7 @@ __metadata: languageName: node linkType: hard -"bignumber.js@npm:^9.1.1, bignumber.js@npm:^9.1.2": +"bignumber.js@npm:^9.1.2": version: 9.1.2 resolution: "bignumber.js@npm:9.1.2" checksum: 10c0/e17786545433f3110b868725c449fa9625366a6e675cd70eb39b60938d6adbd0158cb4b3ad4f306ce817165d37e63f4aa3098ba4110db1d9a3b9f66abfbaf10d @@ -9014,7 +8920,7 @@ __metadata: languageName: node linkType: hard -"nock@npm:^13.5.0, nock@npm:^13.5.5": +"nock@npm:^13.5.4, nock@npm:^13.5.5": version: 13.5.6 resolution: "nock@npm:13.5.6" dependencies: @@ -10889,15 +10795,6 @@ __metadata: languageName: node linkType: hard -"smoldot@npm:2.0.22": - version: 2.0.22 - resolution: "smoldot@npm:2.0.22" - dependencies: - ws: "npm:^8.8.1" - checksum: 10c0/fa439bebfe0d0d46e4da342a313d0653fd56557b6459b5ba3db1fd6bcfb345e9d9577c27e1f6692e67dec0206addb95de6b594c17041729f5689b4b123974495 - languageName: node - linkType: hard - "smoldot@npm:2.0.26": version: 2.0.26 resolution: "smoldot@npm:2.0.26" @@ -11654,7 +11551,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.7.0, tslib@npm:^2.8.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.6.3, tslib@npm:^2.7.0, tslib@npm:^2.8.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 @@ -12721,7 +12618,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.18.0, ws@npm:^8.15.1, ws@npm:^8.18.0, ws@npm:^8.8.1": +"ws@npm:8.18.0, ws@npm:^8.18.0, ws@npm:^8.8.1": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: From b7e12a1c548d7a80a76c18170ce040c983fe1141 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 11:38:45 +0700 Subject: [PATCH 58/84] tx submission via papi --- .../src/canvas/CreatePool/Summary/index.tsx | 41 ++- .../src/canvas/JoinPool/Overview/JoinForm.tsx | 22 +- .../src/canvas/JoinPool/Overview/Stats.tsx | 18 +- .../src/canvas/ManageNominations/index.tsx | 22 +- .../canvas/NominatorSetup/Summary/index.tsx | 28 +- .../PoolMembers/Prompts/UnbondMember.tsx | 18 +- .../PoolMembers/Prompts/WithdrawMember.tsx | 13 +- .../src/contexts/Pools/ActivePool/index.tsx | 10 +- .../app/src/contexts/WalletConnect/index.tsx | 26 +- packages/app/src/hooks/useBatchCall/index.tsx | 43 +-- .../app/src/hooks/useBatchCallPapi/index.tsx | 49 --- .../src/hooks/useBondGreatestFee/index.tsx | 8 +- .../app/src/hooks/useBuildPayload/index.tsx | 113 ------- .../app/src/hooks/useProxySupported/index.tsx | 21 +- .../src/hooks/useProxySupportedPapi/index.tsx | 66 ---- .../src/hooks/useSubmitExtrinsic/index.tsx | 215 ++++++-------- .../app/src/hooks/useSubmitExtrinsic/types.ts | 4 + .../hooks/useSubmitExtrinsicPapi/index.tsx | 281 ------------------ .../src/hooks/useSubmitExtrinsicPapi/types.ts | 24 -- .../ManualSign/WalletConnect/index.tsx | 10 +- packages/app/src/modals/Bond/index.tsx | 20 +- .../app/src/modals/ClaimPayouts/Forms.tsx | 21 +- packages/app/src/modals/ClaimReward/index.tsx | 22 +- .../src/modals/ManageFastUnstake/index.tsx | 14 +- .../Forms/ClaimCommission/index.tsx | 11 +- .../ManagePool/Forms/LeavePool/index.tsx | 17 +- .../Forms/ManageCommission/index.tsx | 58 ++-- .../ManagePool/Forms/RenamePool/index.tsx | 16 +- .../Forms/SetClaimPermission/index.tsx | 16 +- .../ManagePool/Forms/SetPoolState/index.tsx | 25 +- .../app/src/modals/StopNominations/index.tsx | 14 +- packages/app/src/modals/Unbond/index.tsx | 15 +- .../app/src/modals/UnlockChunks/Forms.tsx | 28 +- packages/app/src/modals/Unstake/index.tsx | 17 +- packages/app/src/modals/UpdatePayee/index.tsx | 20 +- 35 files changed, 465 insertions(+), 881 deletions(-) delete mode 100644 packages/app/src/hooks/useBatchCallPapi/index.tsx delete mode 100644 packages/app/src/hooks/useBuildPayload/index.tsx delete mode 100644 packages/app/src/hooks/useProxySupportedPapi/index.tsx delete mode 100644 packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx delete mode 100644 packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts diff --git a/packages/app/src/canvas/CreatePool/Summary/index.tsx b/packages/app/src/canvas/CreatePool/Summary/index.tsx index ff55af33c..149cd7058 100644 --- a/packages/app/src/canvas/CreatePool/Summary/index.tsx +++ b/packages/app/src/canvas/CreatePool/Summary/index.tsx @@ -10,7 +10,6 @@ import { useBondedPools } from 'contexts/Pools/BondedPools'; import { useSetup } from 'contexts/Setup'; import { Warning } from 'library/Form/Warning'; import { useBatchCall } from 'hooks/useBatchCall'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Header } from 'library/SetupSteps/Header'; import { MotionContainer } from 'library/SetupSteps/MotionContainer'; import type { SetupStepProps } from 'library/SetupSteps/types'; @@ -21,14 +20,17 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { SummaryWrapper } from './Wrapper'; import { useOverlay } from 'kits/Overlay/Provider'; +import { ApiController } from 'controllers/Api'; +import { Binary } from 'polkadot-api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const Summary = ({ section }: SetupStepProps) => { const { t } = useTranslation('pages'); const { - api, poolsConfig: { lastPoolId }, } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { newBatchCall } = useBatchCall(); @@ -45,7 +47,8 @@ export const Summary = ({ section }: SetupStepProps) => { const { metadata, bond, roles, nominations } = progress; const getTxs = () => { - if (!activeAccount || !api) { + const { pApi } = ApiController.get(network); + if (!activeAccount || !pApi) { return null; } @@ -56,14 +59,30 @@ export const Summary = ({ section }: SetupStepProps) => { const bondToSubmit = unitToPlanck(bond, units).toString(); const txs = [ - api.tx.nominationPools.create( - bondToSubmit, - roles?.root || activeAccount, - roles?.nominator || activeAccount, - roles?.bouncer || activeAccount - ), - api.tx.nominationPools.nominate(poolId.toString(), targetsToSubmit), - api.tx.nominationPools.setMetadata(poolId.toString(), metadata), + pApi.tx.NominationPools.create({ + amount: BigInt(bondToSubmit.toString()), + root: { + type: 'Id', + value: roles?.root || activeAccount, + }, + nominator: { + type: 'Id', + value: roles?.nominator || activeAccount, + }, + bouncer: { + type: 'Id', + value: roles?.bouncer || activeAccount, + }, + }), + + pApi.tx.NominationPools.nominate({ + pool_id: poolId.toNumber(), + validators: targetsToSubmit, + }), + pApi.tx.NominationPools.set_metadata({ + pool_id: poolId.toNumber(), + metadata: Binary.fromHex(metadata), + }), ]; return newBatchCall(txs, activeAccount); }; diff --git a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx index 31024e3fd..51ac1f894 100644 --- a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx +++ b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx @@ -12,9 +12,6 @@ import { JoinFormWrapper } from '../Wrappers'; import { ClaimPermissionInput } from 'library/Form/ClaimPermissionInput'; import { BondFeedback } from 'library/Form/Bond/BondFeedback'; import { useBondGreatestFee } from 'hooks/useBondGreatestFee'; -import { useApi } from 'contexts/Api'; -import { useBatchCall } from 'hooks/useBatchCall'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useOverlay } from 'kits/Overlay/Provider'; import { useSetup } from 'contexts/Setup'; import { defaultPoolProgress } from 'contexts/Setup/defaults'; @@ -24,11 +21,14 @@ import type { OverviewSectionProps } from '../types'; import { defaultClaimPermission } from 'controllers/ActivePools/defaults'; import { useTranslation } from 'react-i18next'; import { planckToUnitBn } from 'library/Utils'; +import { ApiController } from 'controllers/Api'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { const { t } = useTranslation(); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { @@ -72,8 +72,9 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { // Get transaction for submission. const getTx = () => { + const { pApi } = ApiController.get(network); const tx = null; - if (!api || !claimPermission || !formValid) { + if (!pApi || !claimPermission || !formValid) { return tx; } @@ -81,11 +82,18 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { !bondValid ? '0' : bond.bond, units ).toString(); - const txs = [api.tx.nominationPools.join(bondToSubmit, bondedPool.id)]; + const txs = [ + pApi.tx.NominationPools.join({ + amount: BigInt(bondToSubmit), + pool_id: bondedPool.id, + }), + ]; // If claim permission is not the default, add it to tx. if (claimPermission !== defaultClaimPermission) { - txs.push(api.tx.nominationPools.setClaimPermission(claimPermission)); + txs.push( + pApi.tx.NominationPools.set_claim_permission({ type: claimPermission }) + ); } if (txs.length === 1) { diff --git a/packages/app/src/canvas/JoinPool/Overview/Stats.tsx b/packages/app/src/canvas/JoinPool/Overview/Stats.tsx index f7b9e69e5..4722836a0 100644 --- a/packages/app/src/canvas/JoinPool/Overview/Stats.tsx +++ b/packages/app/src/canvas/JoinPool/Overview/Stats.tsx @@ -15,6 +15,7 @@ import { StyledLoader } from 'library/PoolSync/Loader'; import type { CSSProperties } from 'styled-components'; import { PoolSync } from 'library/PoolSync'; import { planckToUnitBn } from 'library/Utils'; +import { ApiController } from 'controllers/Api'; export const Stats = ({ bondedPool, @@ -23,13 +24,14 @@ export const Stats = ({ }: OverviewSectionProps) => { const { t } = useTranslation('library'); const { + network, networkData: { units, unit, brand: { token: Token }, }, } = useNetwork(); - const { isReady, api } = useApi(); + const { isReady } = useApi(); const { getPoolRewardPoints } = usePoolPerformance(); const poolRewardPoints = getPoolRewardPoints(performanceKey); const rawEraRewardPoints = Object.values( @@ -41,16 +43,16 @@ export const Stats = ({ // Fetches the balance of the bonded pool. const getPoolBalance = async () => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return; } - const balance = ( - await api.call.nominationPoolsApi.pointsToBalance( - bondedPool.id, - rmCommas(bondedPool.points) - ) - ).toString(); + const apiResult = await pApi.apis.NominationPoolsApi.points_to_balance( + bondedPool.id, + BigInt(rmCommas(bondedPool.points)) + ); + const balance = new BigNumber(apiResult?.toString() || 0); if (balance) { setPoolBalance(new BigNumber(balance)); diff --git a/packages/app/src/canvas/ManageNominations/index.tsx b/packages/app/src/canvas/ManageNominations/index.tsx index 435806903..105d08abe 100644 --- a/packages/app/src/canvas/ManageNominations/index.tsx +++ b/packages/app/src/canvas/ManageNominations/index.tsx @@ -10,7 +10,6 @@ import { useApi } from 'contexts/Api'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { usePrompt } from 'contexts/Prompt'; import { useHelp } from 'contexts/Help'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBonded } from 'contexts/Bonded'; import { useActivePool } from 'contexts/Pools/ActivePool'; @@ -24,6 +23,9 @@ import { RevertPrompt } from './Prompts/RevertPrompt'; import { CanvasSubmitTxFooter, CanvasFullScreenWrapper } from '../Wrappers'; import { NotificationsController } from 'controllers/Notifications'; import { ButtonHelp, ButtonPrimary, ButtonPrimaryInvert } from 'ui-buttons'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const ManageNominations = () => { const { t } = useTranslation('library'); @@ -32,8 +34,9 @@ export const ManageNominations = () => { setCanvasStatus, config: { options }, } = useOverlay().canvas; + const { consts } = useApi(); const { openHelp } = useHelp(); - const { consts, api } = useApi(); + const { network } = useNetwork(); const { activePool } = useActivePool(); const { getBondedAccount } = useBonded(); const { activeAccount } = useActiveAccounts(); @@ -90,8 +93,9 @@ export const ManageNominations = () => { // Tx to submit. const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } @@ -100,16 +104,22 @@ export const ManageNominations = () => { isPool ? nominee.address : { - Id: nominee.address, + type: 'Id', + value: nominee.address, } ); if (isPool) { if (activePool) { - tx = api.tx.nominationPools.nominate(activePool.id, targetsToSubmit); + tx = pApi.tx.NominationPools.nominate({ + pool_id: activePool.id, + validators: targetsToSubmit, + }); } } else { - tx = api.tx.staking.nominate(targetsToSubmit); + tx = pApi.tx.Staking.nominate({ + targets: targetsToSubmit, + }); } return tx; }; diff --git a/packages/app/src/canvas/NominatorSetup/Summary/index.tsx b/packages/app/src/canvas/NominatorSetup/Summary/index.tsx index 5a1bf88ce..8555e6de6 100644 --- a/packages/app/src/canvas/NominatorSetup/Summary/index.tsx +++ b/packages/app/src/canvas/NominatorSetup/Summary/index.tsx @@ -8,24 +8,24 @@ import BigNumber from 'bignumber.js'; import { useTranslation } from 'react-i18next'; import { useSetup } from 'contexts/Setup'; import { Warning } from 'library/Form/Warning'; -import { useBatchCall } from 'hooks/useBatchCall'; import { usePayeeConfig } from 'hooks/usePayeeConfig'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Header } from 'library/SetupSteps/Header'; import { MotionContainer } from 'library/SetupSteps/MotionContainer'; import type { SetupStepProps } from 'library/SetupSteps/types'; import { SubmitTx } from 'library/SubmitTx'; import { useNetwork } from 'contexts/Network'; -import { useApi } from 'contexts/Api'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { SummaryWrapper } from './Wrapper'; import { useOverlay } from 'kits/Overlay/Provider'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const Summary = ({ section }: SetupStepProps) => { const { t } = useTranslation('pages'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { newBatchCall } = useBatchCall(); @@ -40,28 +40,36 @@ export const Summary = ({ section }: SetupStepProps) => { const { bond, nominations, payee } = progress; const getTxs = () => { - if (!activeAccount || !api) { + const { pApi } = ApiController.get(network); + if (!activeAccount || !pApi) { return null; } const targetsToSubmit = nominations.map( ({ address }: { address: string }) => ({ - Id: address, + type: 'Id', + value: address, }) ); const payeeToSubmit = payee.destination === 'Account' ? { - Account: payee.account, + type: 'Account', + value: payee.account, } - : payee.destination; + : { + type: payee.destination, + }; const bondToSubmit = unitToPlanck(bond || '0', units).toString(); const txs = [ - api.tx.staking.bond(bondToSubmit, payeeToSubmit), - api.tx.staking.nominate(targetsToSubmit), + pApi.tx.Staking.bond({ + value: BigInt(bondToSubmit), + payee: payeeToSubmit, + }), + pApi.tx.Staking.nominate({ targets: targetsToSubmit }), ]; return newBatchCall(txs, activeAccount); }; diff --git a/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx b/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx index 8a5bf0900..e51b479e9 100644 --- a/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx +++ b/packages/app/src/canvas/PoolMembers/Prompts/UnbondMember.tsx @@ -11,7 +11,6 @@ import { useApi } from 'contexts/Api'; import { Warning } from 'library/Form/Warning'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { timeleftAsString, planckToUnitBn } from 'library/Utils'; import { SubmitTx } from 'library/SubmitTx'; import { StaticNote } from 'modals/Utils/StaticNote'; @@ -23,6 +22,8 @@ import { Title } from 'library/Prompt/Title'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ModalNotes } from 'kits/Overlay/structure/ModalNotes'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const UnbondMember = ({ who, @@ -32,8 +33,9 @@ export const UnbondMember = ({ member: PoolMembership; }) => { const { t } = useTranslation('modals'); - const { api, consts } = useApi(); + const { consts } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { closePrompt } = usePrompt(); @@ -71,8 +73,9 @@ export const UnbondMember = ({ // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } // remove decimal errors @@ -80,7 +83,14 @@ export const UnbondMember = ({ !bondValid ? '0' : bond.bond, units ).toString(); - tx = api.tx.nominationPools.unbond(who, bondToSubmit); + + tx = pApi.tx.NominationPools.unbond({ + member_account: { + type: 'Id', + value: who, + }, + unbonding_points: BigInt(bondToSubmit), + }); return tx; }; diff --git a/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx b/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx index b508a029e..715a280c8 100644 --- a/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx +++ b/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx @@ -10,7 +10,6 @@ import { useApi } from 'contexts/Api'; import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -22,6 +21,8 @@ import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ModalNotes } from 'kits/Overlay/structure/ModalNotes'; import { planckToUnitBn } from 'library/Utils'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const WithdrawMember = ({ who, @@ -34,6 +35,7 @@ export const WithdrawMember = ({ }) => { const { t } = useTranslation('modals'); const { + network, networkData: { units, unit }, } = useNetwork(); const { closePrompt } = usePrompt(); @@ -65,11 +67,18 @@ export const WithdrawMember = ({ // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; if (!valid || !api) { return tx; } - tx = api.tx.nominationPools.withdrawUnbonded(who, historyDepth.toString()); + tx = pApi.tx.NominationPools.withdraw_unbonded({ + member_account: { + type: 'Id', + value: who, + }, + num_slashing_spans: historyDepth.toNumber(), + }); return tx; }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/contexts/Pools/ActivePool/index.tsx b/packages/app/src/contexts/Pools/ActivePool/index.tsx index ae5f69890..b95a0281d 100644 --- a/packages/app/src/contexts/Pools/ActivePool/index.tsx +++ b/packages/app/src/contexts/Pools/ActivePool/index.tsx @@ -16,6 +16,7 @@ import { defaultActivePoolContext, defaultPoolRoles } from './defaults'; import { SyncController } from 'controllers/Sync'; import { useActivePools } from 'hooks/useActivePools'; import BigNumber from 'bignumber.js'; +import { ApiController } from 'controllers/Api'; export const ActivePoolContext = createContext( defaultActivePoolContext @@ -187,10 +188,11 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Fetch and update unclaimed pool rewards for an address from runtime call. const fetchPendingRewards = async (address: string | undefined) => { - if (api && address) { - const pendingRewards = - await api.call.nominationPoolsApi.pendingRewards(address); - return new BigNumber(pendingRewards?.toString() || 0); + const { pApi } = ApiController.get(network); + if (pApi && address) { + const apiResult = + await pApi.apis.NominationPoolsApi.pending_rewards(address); + return new BigNumber(apiResult?.toString() || 0); } return new BigNumber(0); }; diff --git a/packages/app/src/contexts/WalletConnect/index.tsx b/packages/app/src/contexts/WalletConnect/index.tsx index 8d0983a47..c67666d82 100644 --- a/packages/app/src/contexts/WalletConnect/index.tsx +++ b/packages/app/src/contexts/WalletConnect/index.tsx @@ -12,6 +12,7 @@ import { getSdkError } from '@walletconnect/utils'; import { getUnixTime } from 'date-fns'; import { useApi } from 'contexts/Api'; import { useNetwork } from 'contexts/Network'; +import { ApiController } from 'controllers/Api'; export const WalletConnectContext = createContext(defaults.defaultWalletConnect); @@ -27,10 +28,13 @@ export const WalletConnectProvider = ({ children: ReactNode; }) => { const { network } = useNetwork(); - const { isReady, api } = useApi(); + const { + isReady, + chainSpecs: { genesisHash }, + } = useApi(); // Check if the API is present. - const apiPresent = api !== null; + const apiPresent = !!ApiController.get(network); // The WalletConnect provider. const wcProvider = useRef(null); @@ -87,7 +91,7 @@ export const WalletConnectProvider = ({ // Connect WalletConnect provider and retrieve metadata. const connectProvider = async () => { - if (!wcInitialized || !api) { + if (!wcInitialized) { return; } @@ -99,9 +103,7 @@ export const WalletConnectProvider = ({ // Update most recent connected chain. sessionChain.current = network; - const caips = [ - `polkadot:${api.genesisHash.toHex().substring(2).substring(0, 32)}`, - ]; + const caips = [`polkadot:${genesisHash.substring(2).substring(0, 32)}`]; // If there are no chains connected, return early. if (!caips.length) { @@ -159,15 +161,12 @@ export const WalletConnectProvider = ({ // Update session namespaces. NOTE: This method is currently not in use due to a // default chain error upon reconnecting to the session. const updateWcSession = async () => { - if (!wcInitialized || !api) { + if (!wcInitialized) { return; } // Update most recent connected chains. sessionChain.current = network; - - const caips = [ - `polkadot:${api.genesisHash.toHex().substring(2).substring(0, 32)}`, - ]; + const caips = [`polkadot:${genesisHash.substring(2).substring(0, 32)}`]; // If there are no chains connected, return early. if (!caips.length) { @@ -279,9 +278,6 @@ export const WalletConnectProvider = ({ }; const fetchAddresses = async (): Promise => { - if (!api) { - return []; - } // Retrieve a new session or get current one. const wcSession = await initializeWcSession(); if (wcSession === null) { @@ -293,7 +289,7 @@ export const WalletConnectProvider = ({ .map((namespace: AnyJson) => namespace.accounts) .flat(); - const caip = api.genesisHash.toHex().substring(2).substring(0, 32); + const caip = genesisHash.substring(2).substring(0, 32); // Only get accounts for the currently selected `caip`. let filteredAccounts = walletConnectAccounts.filter((wcAccount) => { diff --git a/packages/app/src/hooks/useBatchCall/index.tsx b/packages/app/src/hooks/useBatchCall/index.tsx index db4f281c7..1c882d47e 100644 --- a/packages/app/src/hooks/useBatchCall/index.tsx +++ b/packages/app/src/hooks/useBatchCall/index.tsx @@ -1,37 +1,46 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { useApi } from 'contexts/Api'; import type { AnyApi, MaybeAddress } from 'types'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; import { useProxySupported } from 'hooks/useProxySupported'; +import type { UnsafeTx } from 'hooks/useSubmitExtrinsic/types'; export const useBatchCall = () => { - const { api } = useApi(); + const { network } = useNetwork(); const { activeProxy } = useActiveAccounts(); const { isProxySupported } = useProxySupported(); - const newBatchCall = (txs: AnyApi[], from: MaybeAddress): AnyApi => { - if (!api) { + const newBatchCall = (txs: UnsafeTx[], from: MaybeAddress): AnyApi => { + const { pApi } = ApiController.get(network); + + if (!pApi) { return undefined; } from = from || ''; + const batchTx = pApi.tx.Utility.batch({ + calls: txs.map((tx) => tx.decodedCall), + }); - if (activeProxy && isProxySupported(api.tx.utility.batch(txs), from)) { - return api?.tx.utility.batch( - txs.map((tx) => - api.tx.proxy.proxy( - { - id: from, - }, - null, - tx - ) - ) - ); + if (activeProxy && isProxySupported(batchTx, from)) { + return pApi.tx.Utility.batch({ + calls: txs.map( + (tx) => + pApi.tx.Proxy.proxy({ + real: { + type: 'Id', + value: from, + }, + force_proxy_type: null, + call: tx.decodedCall, + }).decodedCall + ), + }); } - return api?.tx.utility.batch(txs); + return batchTx; }; return { diff --git a/packages/app/src/hooks/useBatchCallPapi/index.tsx b/packages/app/src/hooks/useBatchCallPapi/index.tsx deleted file mode 100644 index 131e35f11..000000000 --- a/packages/app/src/hooks/useBatchCallPapi/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { AnyApi, MaybeAddress } from 'types'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { ApiController } from 'controllers/Api'; -import { useNetwork } from 'contexts/Network'; -import { useProxySupportedPapi } from 'hooks/useProxySupportedPapi'; -import type { UnsafeTx } from 'hooks/useSubmitExtrinsicPapi/types'; - -export const useBatchCallPapi = () => { - const { network } = useNetwork(); - const { activeProxy } = useActiveAccounts(); - const { isProxySupported } = useProxySupportedPapi(); - - const newBatchCall = (txs: UnsafeTx[], from: MaybeAddress): AnyApi => { - const { pApi } = ApiController.get(network); - - if (!pApi) { - return undefined; - } - - from = from || ''; - const batchTx = pApi.tx.Utility.batch({ - calls: txs.map((tx) => tx.decodedCall), - }); - - if (activeProxy && isProxySupported(batchTx, from)) { - return pApi.tx.Utility.batch({ - calls: txs.map( - (tx) => - pApi.tx.Proxy.proxy({ - real: { - type: 'Id', - value: from, - }, - force_proxy_type: null, - call: tx.decodedCall, - }).decodedCall - ), - }); - } - return batchTx; - }; - - return { - newBatchCall, - }; -}; diff --git a/packages/app/src/hooks/useBondGreatestFee/index.tsx b/packages/app/src/hooks/useBondGreatestFee/index.tsx index 651cc451a..d343e3e95 100644 --- a/packages/app/src/hooks/useBondGreatestFee/index.tsx +++ b/packages/app/src/hooks/useBondGreatestFee/index.tsx @@ -48,11 +48,13 @@ export const useBondGreatestFee = ({ bondFor }: { bondFor: BondFor }) => { } if (bondFor === 'pool') { tx = pApi.tx.NominationPools.bond_extra({ - type: 'FreeBalance', - value: BigInt(bond), + extra: { + type: 'FreeBalance', + value: BigInt(bond), + }, }); } else if (bondFor === 'nominator') { - tx = pApi.tx.Staking.bond_extra({ bond }); + tx = pApi.tx.Staking.bond_extra({ max_additional: BigInt(bond) }); } if (tx) { diff --git a/packages/app/src/hooks/useBuildPayload/index.tsx b/packages/app/src/hooks/useBuildPayload/index.tsx deleted file mode 100644 index dba77380f..000000000 --- a/packages/app/src/hooks/useBuildPayload/index.tsx +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata'; -import type { ApiPromise } from '@polkadot/api'; -import { u8aToHex, objectSpread } from '@polkadot/util'; -import type { AnyJson } from '@w3ux/types'; -import { useApi } from 'contexts/Api'; -import { useBalances } from 'contexts/Balances'; -import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; -import { useTxMeta } from 'contexts/TxMeta'; -import type { AnyApi } from 'types'; - -export const useBuildPayload = () => { - const { api } = useApi(); - const { getNonce } = useBalances(); - const { setTxPayload } = useTxMeta(); - const { getAccount } = useImportedAccounts(); - - // Build metadata hash and return updated payload. - const fetchMetadataHash = async (a: ApiPromise, p: AnyJson) => { - const metadata = await a.call.metadata.metadataAtVersion(15); - const { specName, specVersion } = a.runtimeVersion; - - const opts = { - base58Prefix: Number(a.consts.system.ss58Prefix.toString()), - decimals: a.registry.chainDecimals[0], - specName: specName.toString(), - specVersion: specVersion.toNumber(), - tokenSymbol: a.registry.chainTokens[0], - }; - - const merkleizedMetadata = merkleizeMetadata(metadata.toHex(), opts); - const metadataHash = u8aToHex(merkleizedMetadata.digest()); - const payload = objectSpread({}, p, { - metadataHash, - mode: 1, - withSignedTransaction: true, - }); - const newPayload = a.registry.createType('ExtrinsicPayload', payload); - - return { - newPayload, - newTxMetadata: merkleizedMetadata.getProofForExtrinsicPayload( - u8aToHex(newPayload.toU8a(true)) - ), - }; - }; - - // Build and set payload of the transaction and store it in TxMetaContext. - const buildPayload = async (tx: AnyApi, from: string, uid: number) => { - if (api && tx) { - const accountMeta = getAccount(from); - const source = accountMeta?.source; - - const lastHeader = await api.rpc.chain.getHeader(); - const blockNumber = api.registry.createType( - 'BlockNumber', - lastHeader.number.toNumber() - ); - const method = api.createType('Call', tx); - const era = api.registry.createType('ExtrinsicEra', { - current: lastHeader.number.toNumber(), - period: 64, - }); - - const accountNonce = getNonce(from); - const nonce = api.registry.createType('Compact', accountNonce); - - // Construct the payload value. - const payloadJson: AnyJson = { - specVersion: api.runtimeVersion.specVersion.toHex(), - transactionVersion: api.runtimeVersion.transactionVersion.toHex(), - runtimeVersion: api.runtimeVersion, - version: api.extrinsicVersion, - address: from, - blockHash: lastHeader.hash.toHex(), - blockNumber: blockNumber.toHex(), - era: era.toHex(), - genesisHash: api.genesisHash.toHex(), - method: method.toHex(), - nonce: nonce.toHex(), - signedExtensions: api.registry.signedExtensions, - tip: api.registry.createType('Compact', 0).toHex(), - }; - - let payload; - let txMetadata = null; - - // If the source is `ledger`, add the metadata hash to the payload. - if (source === 'ledger') { - const { newPayload, newTxMetadata } = await fetchMetadataHash( - api, - payloadJson - ); - payload = newPayload; - txMetadata = newTxMetadata; - } else { - // Create the payload raw. - payload = api.registry.createType('ExtrinsicPayload', payloadJson, { - version: payloadJson.version, - }); - } - - // Persist both the payload and the payload bytes in state, indexed by its uid. - setTxPayload(txMetadata, payload, payloadJson, uid); - } - }; - - return { - buildPayload, - }; -}; diff --git a/packages/app/src/hooks/useProxySupported/index.tsx b/packages/app/src/hooks/useProxySupported/index.tsx index d45ebf232..7fea61584 100644 --- a/packages/app/src/hooks/useProxySupported/index.tsx +++ b/packages/app/src/hooks/useProxySupported/index.tsx @@ -8,8 +8,9 @@ import { import { useBonded } from 'contexts/Bonded'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useProxies } from 'contexts/Proxies'; -import type { AnyApi, MaybeAddress } from 'types'; +import type { MaybeAddress } from 'types'; import type { AnyJson } from '@w3ux/types'; +import type { UnsafeTx } from 'hooks/useSubmitExtrinsic/types'; export const useProxySupported = () => { const { getBondedAccount } = useBonded(); @@ -22,32 +23,32 @@ export const useProxySupported = () => { UnsupportedIfUniqueController.includes(c) && getBondedAccount(f) !== f; // Determine whether the provided tx is proxy supported. - const isProxySupported = (tx: AnyApi, delegator: MaybeAddress) => { + const isProxySupported = (tx: UnsafeTx, delegator: MaybeAddress) => { // if already wrapped, return. if ( - tx?.method.toHuman().section === 'proxy' && - tx?.method.toHuman().method === 'proxy' + tx?.decodedCall.type === 'Proxy' && + tx?.decodedCall.value.type === 'proxy' ) { return true; } const proxyDelegate = getProxyDelegate(delegator, activeProxy); const proxyType = proxyDelegate?.proxyType || ''; - const pallet = tx?.method.toHuman().section; - const method = tx?.method.toHuman().method; + const pallet: string = (tx?.decodedCall.type || '').toLowerCase(); + const method: string = (tx?.decodedCall.value.type || '').toLowerCase(); const call = `${pallet}.${method}`; // If a batch call, test if every inner call is a supported proxy call. if (call === 'utility.batch') { - return (tx?.method?.toHuman()?.args?.calls || []) + return (tx?.decodedCall.value?.value?.calls || []) .map((c: AnyJson) => ({ - pallet: c.section, - method: c.method, + pallet: c.type, + method: c.value.type, })) .every( (c: AnyJson) => (isSupportedProxyCall(proxyType, c.pallet, c.method) || - (c.pallet === 'proxy' && c.method === 'proxy')) && + (c.pallet === 'Proxy' && c.method === 'proxy')) && !controllerNotSupported(`${pallet}.${method}`, delegator) ); } diff --git a/packages/app/src/hooks/useProxySupportedPapi/index.tsx b/packages/app/src/hooks/useProxySupportedPapi/index.tsx deleted file mode 100644 index 0c3e12a6c..000000000 --- a/packages/app/src/hooks/useProxySupportedPapi/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { - UnsupportedIfUniqueController, - isSupportedProxyCall, -} from 'config/proxies'; -import { useBonded } from 'contexts/Bonded'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useProxies } from 'contexts/Proxies'; -import type { MaybeAddress } from 'types'; -import type { AnyJson } from '@w3ux/types'; -import type { UnsafeTx } from 'hooks/useSubmitExtrinsicPapi/types'; - -export const useProxySupportedPapi = () => { - const { getBondedAccount } = useBonded(); - const { getProxyDelegate } = useProxies(); - const { activeProxy } = useActiveAccounts(); - - // If call is from controller, & controller is different from stash, then proxy is not - // supported. - const controllerNotSupported = (c: string, f: MaybeAddress) => - UnsupportedIfUniqueController.includes(c) && getBondedAccount(f) !== f; - - // Determine whether the provided tx is proxy supported. - const isProxySupported = (tx: UnsafeTx, delegator: MaybeAddress) => { - // if already wrapped, return. - if ( - tx?.decodedCall.type === 'Proxy' && - tx?.decodedCall.value.type === 'proxy' - ) { - return true; - } - - const proxyDelegate = getProxyDelegate(delegator, activeProxy); - const proxyType = proxyDelegate?.proxyType || ''; - const pallet: string = (tx?.decodedCall.type || '').toLowerCase(); - const method: string = (tx?.decodedCall.value.type || '').toLowerCase(); - const call = `${pallet}.${method}`; - - // If a batch call, test if every inner call is a supported proxy call. - if (call === 'utility.batch') { - return (tx?.decodedCall.value?.value?.calls || []) - .map((c: AnyJson) => ({ - pallet: c.type, - method: c.value.type, - })) - .every( - (c: AnyJson) => - (isSupportedProxyCall(proxyType, c.pallet, c.method) || - (c.pallet === 'Proxy' && c.method === 'proxy')) && - !controllerNotSupported(`${pallet}.${method}`, delegator) - ); - } - - // Check if the current call is a supported proxy call. - return ( - isSupportedProxyCall(proxyType, pallet, method) && - !controllerNotSupported(call, delegator) - ); - }; - - return { - isProxySupported, - }; -}; diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 4b969a395..b413b04ad 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -5,17 +5,24 @@ import BigNumber from 'bignumber.js'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { DappName, ManualSigners } from 'consts'; -import { useApi } from 'contexts/Api'; import { useLedgerHardware } from 'contexts/LedgerHardware'; import { useTxMeta } from 'contexts/TxMeta'; -import type { AnyApi } from 'types'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; -import { useBuildPayload } from '../useBuildPayload'; -import { useProxySupported } from '../useProxySupported'; -import type { UseSubmitExtrinsic, UseSubmitExtrinsicProps } from './types'; +import type { + UnsafeTx, + UseSubmitExtrinsic, + UseSubmitExtrinsicProps, +} from './types'; import { NotificationsController } from 'controllers/Notifications'; import { useExtensions } from '@w3ux/react-connect-kit'; +import { useProxySupported } from 'hooks/useProxySupported'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useBalances } from 'contexts/Balances'; +import { InvalidTxError } from 'polkadot-api'; +import { connectInjectedExtension } from 'polkadot-api/pjs-signer'; +import { formatAccountSs58 } from '@w3ux/utils'; export const useSubmitExtrinsic = ({ tx, @@ -25,27 +32,18 @@ export const useSubmitExtrinsic = ({ callbackInBlock, }: UseSubmitExtrinsicProps): UseSubmitExtrinsic => { const { t } = useTranslation('library'); - const { api } = useApi(); - const { buildPayload } = useBuildPayload(); + const { network } = useNetwork(); + const { getNonce } = useBalances(); const { activeProxy } = useActiveAccounts(); const { extensionsStatus } = useExtensions(); const { isProxySupported } = useProxySupported(); const { handleResetLedgerTask } = useLedgerHardware(); const { addPendingNonce, removePendingNonce } = useTxMeta(); const { getAccount, requiresManualSign } = useImportedAccounts(); - const { - txFees, - setTxFees, - setSender, - getTxPayload, - getTxSignature, - setTxSignature, - resetTxPayload, - incrementPayloadUid, - } = useTxMeta(); + const { txFees, setTxFees, setSender, incrementPayloadUid } = useTxMeta(); // Store given tx as a ref. - const txRef = useRef(tx); + const txRef = useRef(tx); // Store given submit address as a ref. const fromRef = useRef(from || ''); @@ -61,10 +59,12 @@ export const useSubmitExtrinsic = ({ // If proxy account is active, wrap tx in a proxy call and set the sender to the proxy account. const wrapTxIfActiveProxy = () => { + const { pApi } = ApiController.get(network); + // if already wrapped, update fromRef and return. if ( - txRef.current?.method.toHuman().section === 'proxy' && - txRef.current?.method.toHuman().method === 'proxy' + txRef.current?.decodedCall.type === 'Proxy' && + txRef.current?.decodedCall.value.type === 'proxy' ) { if (activeProxy) { fromRef.current = activeProxy; @@ -73,7 +73,7 @@ export const useSubmitExtrinsic = ({ } if ( - api && + pApi && activeProxy && txRef.current && isProxySupported(txRef.current, fromRef.current) @@ -84,20 +84,21 @@ export const useSubmitExtrinsic = ({ // Do not wrap batch transactions. Proxy calls should already be wrapping each tx within the // batch via `useBatchCall`. if ( - txRef.current?.method.toHuman().section === 'utility' && - txRef.current?.method.toHuman().method === 'batch' + txRef.current?.decodedCall.type === 'Utility' && + txRef.current?.decodedCall.value.type === 'batch' ) { return; } // Not a batch transaction: wrap tx in proxy call. - txRef.current = api.tx.proxy.proxy( - { - id: from, + txRef.current = pApi.tx.Proxy.proxy({ + real: { + type: 'Id', + value: from, }, - null, - txRef.current - ); + forceProxyType: null, + call: txRef.current.decodedCall, + }); } }; @@ -106,8 +107,10 @@ export const useSubmitExtrinsic = ({ if (txRef.current === null) { return; } + // get payment info - const { partialFee } = await txRef.current.paymentInfo(fromRef.current); + const partialFee = (await txRef.current.getPaymentInfo(fromRef.current)) + .partial_fee; const partialFeeBn = new BigNumber(partialFee.toString()); // give tx fees to global useTxMeta context @@ -119,24 +122,16 @@ export const useSubmitExtrinsic = ({ // Extrinsic submission handler. const onSubmit = async () => { const account = getAccount(fromRef.current); - if ( - account === null || - submitting || - !shouldSubmit || - !api || - (requiresManualSign(fromRef.current) && !getTxSignature()) - ) { + if (account === null || submitting || !shouldSubmit) { return; } - const nonce = ( - await api.rpc.system.accountNextIndex(fromRef.current) - ).toHuman(); - + const nonce = String(getNonce(fromRef.current)); const { source } = account; + const isManualSigner = ManualSigners.includes(source); // if `activeAccount` is imported from an extension, ensure it is enabled. - if (!ManualSigners.includes(source)) { + if (!isManualSigner) { const isInstalled = Object.entries(extensionsStatus).find( ([id, status]) => id === source && status === 'connected' ); @@ -176,31 +171,26 @@ export const useSubmitExtrinsic = ({ } }; - const onFinalizedEvent = (method: string) => { - if (method === 'ExtrinsicSuccess') { - NotificationsController.emit({ - title: t('finalized'), - subtitle: t('transactionSuccessful'), - }); - } else if (method === 'ExtrinsicFailed') { - NotificationsController.emit({ - title: t('failed'), - subtitle: t('errorWithTransaction'), - }); - setSubmitting(false); - removePendingNonce(nonce); - } + const onFinalizedEvent = () => { + NotificationsController.emit({ + title: t('finalized'), + subtitle: t('transactionSuccessful'), + }); + setSubmitting(false); + removePendingNonce(nonce); }; - const resetTx = () => { - resetTxPayload(); - setTxSignature(null); + const onFailedTx = () => { + NotificationsController.emit({ + title: t('failed'), + subtitle: t('errorWithTransaction'), + }); setSubmitting(false); + removePendingNonce(nonce); }; - const resetManualTx = () => { - resetTx(); - handleResetLedgerTask(); + const resetTx = () => { + setSubmitting(false); }; const onError = (type?: string) => { @@ -215,80 +205,55 @@ export const useSubmitExtrinsic = ({ }); }; - const handleStatus = (status: AnyApi) => { - if (status.isReady) { + const handleStatus = (status: string) => { + if (status === 'broadcasted') { onReady(); } - if (status.isInBlock) { + if (status === 'txBestBlocksState') { onInBlock(); } }; - const unsubEvents = ['ExtrinsicSuccess', 'ExtrinsicFailed']; - // pre-submission state update setSubmitting(true); - const txPayloadValue = getTxPayload(); - const txSignature = getTxSignature(); - // handle signed transaction. - if (getTxSignature()) { - try { - txRef.current.addSignature( - fromRef.current, - txSignature, - txPayloadValue.toHex() - ); - - const unsub = await txRef.current.send( - ({ status, events = [] }: AnyApi) => { - if (!didTxReset.current) { - didTxReset.current = true; - resetManualTx(); - } - - handleStatus(status); - if (status.isFinalized) { - events.forEach(({ event: { method } }: AnyApi) => { - onFinalizedEvent(method); - if (unsubEvents?.includes(method)) { - unsub(); - } - }); - } - } - ); - } catch (e) { - onError(ManualSigners.includes(source) ? source : 'default'); - } + let signer; + if (requiresManualSign(fromRef.current)) { + // TODO: Get custom signer here. } else { - // handle unsigned transaction. - const { signer } = account; - try { - const unsub = await txRef.current.signAndSend( - fromRef.current, - { signer, withSignedTransaction: true }, - ({ status, events = [] }: AnyApi) => { - if (!didTxReset.current) { - didTxReset.current = true; - resetTx(); - } - - handleStatus(status); - if (status.isFinalized) { - events.forEach(({ event: { method } }: AnyApi) => { - onFinalizedEvent(method); - if (unsubEvents?.includes(method)) { - unsub(); - } - }); - } + // Get the polkadot signer for this account. + signer = (await connectInjectedExtension(source)) + .getAccounts() + .find( + (a) => a.address === formatAccountSs58(fromRef.current, 42) + )?.polkadotSigner; + } + + try { + const sub = tx.signSubmitAndWatch(signer).subscribe({ + next: (result: { type: string }) => { + const eventType = result?.type; + + if (!didTxReset.current) { + didTxReset.current = true; + resetTx(); } - ); - } catch (e) { - onError('default'); - } + handleStatus(eventType); + if (eventType === 'finalized') { + onFinalizedEvent(); + sub?.unsubscribe(); + } + }, + error: (err: Error) => { + if (err instanceof InvalidTxError) { + onFailedTx(); + } + sub?.unsubscribe(); + }, + }); + } catch (e) { + onError('default'); } }; @@ -304,8 +269,6 @@ export const useSubmitExtrinsic = ({ setSender(fromRef.current); // re-calculate estimated tx fee. calculateEstimatedFee(); - // rebuild tx payload. - buildPayload(txRef.current, fromRef.current, uid); }, [tx?.toString(), tx?.method?.args?.calls?.toString(), from]); return { diff --git a/packages/app/src/hooks/useSubmitExtrinsic/types.ts b/packages/app/src/hooks/useSubmitExtrinsic/types.ts index 5eb2febf3..292c83817 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/types.ts +++ b/packages/app/src/hooks/useSubmitExtrinsic/types.ts @@ -1,6 +1,7 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import type { UnsafeTransaction } from 'polkadot-api'; import type { AnyApi, MaybeAddress } from 'types'; export interface UseSubmitExtrinsicProps { @@ -18,3 +19,6 @@ export interface UseSubmitExtrinsic { proxySupported: boolean; submitAddress: MaybeAddress; } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type UnsafeTx = UnsafeTransaction; diff --git a/packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx b/packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx deleted file mode 100644 index 517f9c062..000000000 --- a/packages/app/src/hooks/useSubmitExtrinsicPapi/index.tsx +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import BigNumber from 'bignumber.js'; -import { useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { DappName, ManualSigners } from 'consts'; -import { useLedgerHardware } from 'contexts/LedgerHardware'; -import { useTxMeta } from 'contexts/TxMeta'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; -import type { - UnsafeTx, - UseSubmitExtrinsic, - UseSubmitExtrinsicProps, -} from './types'; -import { NotificationsController } from 'controllers/Notifications'; -import { useExtensions } from '@w3ux/react-connect-kit'; -import { useProxySupportedPapi } from 'hooks/useProxySupportedPapi'; -import { ApiController } from 'controllers/Api'; -import { useNetwork } from 'contexts/Network'; -import { useBalances } from 'contexts/Balances'; -import { InvalidTxError } from 'polkadot-api'; -import { connectInjectedExtension } from 'polkadot-api/pjs-signer'; -import { formatAccountSs58 } from '@w3ux/utils'; - -export const useSubmitExtrinsicPapi = ({ - tx, - from, - shouldSubmit, - callbackSubmit, - callbackInBlock, -}: UseSubmitExtrinsicProps): UseSubmitExtrinsic => { - const { t } = useTranslation('library'); - const { network } = useNetwork(); - const { getNonce } = useBalances(); - const { activeProxy } = useActiveAccounts(); - const { extensionsStatus } = useExtensions(); - const { isProxySupported } = useProxySupportedPapi(); - const { handleResetLedgerTask } = useLedgerHardware(); - const { addPendingNonce, removePendingNonce } = useTxMeta(); - const { getAccount, requiresManualSign } = useImportedAccounts(); - const { txFees, setTxFees, setSender, incrementPayloadUid } = useTxMeta(); - - // Store given tx as a ref. - const txRef = useRef(tx); - - // Store given submit address as a ref. - const fromRef = useRef(from || ''); - - // Store whether the transaction is in progress. - const [submitting, setSubmitting] = useState(false); - - // Store the uid of the extrinsic. - const [uid] = useState(incrementPayloadUid()); - - // Track for one-shot transaction reset after submission. - const didTxReset = useRef(false); - - // If proxy account is active, wrap tx in a proxy call and set the sender to the proxy account. - const wrapTxIfActiveProxy = () => { - const { pApi } = ApiController.get(network); - - // if already wrapped, update fromRef and return. - if ( - txRef.current?.decodedCall.type === 'Proxy' && - txRef.current?.decodedCall.value.type === 'proxy' - ) { - if (activeProxy) { - fromRef.current = activeProxy; - } - return; - } - - if ( - pApi && - activeProxy && - txRef.current && - isProxySupported(txRef.current, fromRef.current) - ) { - // update submit address to active proxy account. - fromRef.current = activeProxy; - - // Do not wrap batch transactions. Proxy calls should already be wrapping each tx within the - // batch via `useBatchCall`. - if ( - txRef.current?.decodedCall.type === 'Utility' && - txRef.current?.decodedCall.value.type === 'batch' - ) { - return; - } - - // Not a batch transaction: wrap tx in proxy call. - txRef.current = pApi.tx.Proxy.proxy({ - real: { - type: 'Id', - value: from, - }, - forceProxyType: null, - call: txRef.current.decodedCall, - }); - } - }; - - // Calculate the estimated tx fee of the transaction. - const calculateEstimatedFee = async () => { - if (txRef.current === null) { - return; - } - - // get payment info - const partialFee = (await txRef.current.getPaymentInfo(fromRef.current)) - .partial_fee; - const partialFeeBn = new BigNumber(partialFee.toString()); - - // give tx fees to global useTxMeta context - if (partialFeeBn.toString() !== txFees.toString()) { - setTxFees(partialFeeBn); - } - }; - - // Extrinsic submission handler. - const onSubmit = async () => { - const account = getAccount(fromRef.current); - if (account === null || submitting || !shouldSubmit) { - return; - } - - const nonce = String(getNonce(fromRef.current)); - const { source } = account; - const isManualSigner = ManualSigners.includes(source); - - // if `activeAccount` is imported from an extension, ensure it is enabled. - if (!isManualSigner) { - const isInstalled = Object.entries(extensionsStatus).find( - ([id, status]) => id === source && status === 'connected' - ); - - if (!isInstalled) { - throw new Error(`${t('walletNotFound')}`); - } - - if (!window?.injectedWeb3?.[source]) { - throw new Error(`${t('walletNotFound')}`); - } - - // summons extension popup if not already connected. - window.injectedWeb3[source].enable(DappName); - } - - const onReady = () => { - addPendingNonce(nonce); - NotificationsController.emit({ - title: t('pending'), - subtitle: t('transactionInitiated'), - }); - if (callbackSubmit && typeof callbackSubmit === 'function') { - callbackSubmit(); - } - }; - - const onInBlock = () => { - setSubmitting(false); - removePendingNonce(nonce); - NotificationsController.emit({ - title: t('inBlock'), - subtitle: t('transactionInBlock'), - }); - if (callbackInBlock && typeof callbackInBlock === 'function') { - callbackInBlock(); - } - }; - - const onFinalizedEvent = () => { - NotificationsController.emit({ - title: t('finalized'), - subtitle: t('transactionSuccessful'), - }); - setSubmitting(false); - removePendingNonce(nonce); - }; - - const onFailedTx = () => { - NotificationsController.emit({ - title: t('failed'), - subtitle: t('errorWithTransaction'), - }); - setSubmitting(false); - removePendingNonce(nonce); - }; - - const resetTx = () => { - setSubmitting(false); - }; - - const onError = (type?: string) => { - resetTx(); - if (type === 'ledger') { - handleResetLedgerTask(); - } - removePendingNonce(nonce); - NotificationsController.emit({ - title: t('cancelled'), - subtitle: t('transactionCancelled'), - }); - }; - - const handleStatus = (status: string) => { - if (status === 'broadcasted') { - onReady(); - } - if (status === 'txBestBlocksState') { - onInBlock(); - } - }; - - // pre-submission state update - setSubmitting(true); - - // handle signed transaction. - let signer; - if (requiresManualSign(fromRef.current)) { - // TODO: Get custom signer here. - } else { - // Get the polkadot signer for this account. - signer = (await connectInjectedExtension(source)) - .getAccounts() - .find( - (a) => a.address === formatAccountSs58(fromRef.current, 42) - )?.polkadotSigner; - } - - try { - const sub = tx.signSubmitAndWatch(signer).subscribe({ - next: (result: { type: string }) => { - const eventType = result?.type; - - if (!didTxReset.current) { - didTxReset.current = true; - resetTx(); - } - handleStatus(eventType); - if (eventType === 'finalized') { - onFinalizedEvent(); - sub?.unsubscribe(); - } - }, - error: (err: Error) => { - if (err instanceof InvalidTxError) { - onFailedTx(); - } - sub?.unsubscribe(); - }, - }); - } catch (e) { - onError('default'); - } - }; - - // Refresh state upon `tx` updates. - useEffect(() => { - // update txRef to latest tx. - txRef.current = tx; - // update submit address to latest from. - fromRef.current = from || ''; - // wrap tx in proxy call if active proxy & proxy supported. - wrapTxIfActiveProxy(); - // ensure sender is up to date. - setSender(fromRef.current); - // re-calculate estimated tx fee. - calculateEstimatedFee(); - }, [tx?.toString(), tx?.method?.args?.calls?.toString(), from]); - - return { - uid, - onSubmit, - submitting, - submitAddress: fromRef.current, - proxySupported: isProxySupported(txRef.current, fromRef.current), - }; -}; diff --git a/packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts b/packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts deleted file mode 100644 index 292c83817..000000000 --- a/packages/app/src/hooks/useSubmitExtrinsicPapi/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import type { UnsafeTransaction } from 'polkadot-api'; -import type { AnyApi, MaybeAddress } from 'types'; - -export interface UseSubmitExtrinsicProps { - tx: AnyApi; - from: MaybeAddress; - shouldSubmit: boolean; - callbackSubmit?: () => void; - callbackInBlock?: () => void; -} - -export interface UseSubmitExtrinsic { - uid: number; - onSubmit: () => void; - submitting: boolean; - proxySupported: boolean; - submitAddress: MaybeAddress; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type UnsafeTx = UnsafeTransaction; diff --git a/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx b/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx index 7644260c4..b1dbdc57d 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx @@ -24,7 +24,9 @@ export const WalletConnect = ({ displayFor, }: SubmitProps & { buttons?: ReactNode[] }) => { const { t } = useTranslation('library'); - const { api } = useApi(); + const { + chainSpecs: { genesisHash }, + } = useApi(); const { accountHasSigner } = useImportedAccounts(); const { wcSessionActive, connectProvider, fetchAddresses, signWcTx } = useWalletConnect(); @@ -49,10 +51,6 @@ export const WalletConnect = ({ buttonPulse = valid; } else { buttonOnClick = async () => { - if (!api) { - return; - } - // If Wallet Connect session is not active, re-connect. if (!wcSessionActive) { await connectProvider(); @@ -68,7 +66,7 @@ export const WalletConnect = ({ setIsSigning(true); - const caip = `polkadot:${api.genesisHash.toHex().substring(2).substring(0, 32)}`; + const caip = `polkadot:${genesisHash.substring(2).substring(0, 32)}`; try { const signature = await signWcTx(caip, payload, sender); diff --git a/packages/app/src/modals/Bond/index.tsx b/packages/app/src/modals/Bond/index.tsx index 6dae88fc7..560d12356 100644 --- a/packages/app/src/modals/Bond/index.tsx +++ b/packages/app/src/modals/Bond/index.tsx @@ -5,14 +5,12 @@ import { unitToPlanck } from '@w3ux/utils'; import BigNumber from 'bignumber.js'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useTransferOptions } from 'contexts/TransferOptions'; import { BondFeedback } from 'library/Form/Bond/BondFeedback'; import { Warning } from 'library/Form/Warning'; import { useBondGreatestFee } from 'hooks/useBondGreatestFee'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; @@ -22,11 +20,13 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { planckToUnitBn } from 'library/Utils'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const Bond = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { notEnoughFunds } = useTxMeta(); @@ -95,8 +95,9 @@ export const Bond = () => { // determine whether this is a pool or staking transaction. const determineTx = (bondToSubmit: BigNumber) => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api) { + if (!pApi) { return tx; } @@ -107,18 +108,21 @@ export const Bond = () => { : bondToSubmit.toString(); if (isPooling) { - tx = api.tx.nominationPools.bondExtra({ - FreeBalance: bondAsString, + tx = pApi.tx.NominationPools.bond_extra({ + extra: { + type: 'FreeBalance', + value: bondAsString, + }, }); } else if (isStaking) { - tx = api.tx.staking.bondExtra(bondAsString); + tx = pApi.tx.Staking.bond_extra({ max_additional: BigInt(bondAsString) }); } return tx; }; // the actual bond tx to submit const getTx = (bondToSubmit: BigNumber) => { - if (!api || !activeAccount) { + if (!activeAccount) { return null; } return determineTx(bondToSubmit); diff --git a/packages/app/src/modals/ClaimPayouts/Forms.tsx b/packages/app/src/modals/ClaimPayouts/Forms.tsx index a95be27d3..db5ad9cfc 100644 --- a/packages/app/src/modals/ClaimPayouts/Forms.tsx +++ b/packages/app/src/modals/ClaimPayouts/Forms.tsx @@ -7,13 +7,10 @@ import BigNumber from 'bignumber.js'; import type { ForwardedRef } from 'react'; import { forwardRef, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from 'kits/Overlay/Provider'; -import { useBatchCall } from 'hooks/useBatchCall'; import type { AnyApi } from 'types'; import { usePayouts } from 'contexts/Payouts'; import { useNetwork } from 'contexts/Network'; @@ -25,6 +22,9 @@ import { ButtonSubmitInvert } from 'ui-buttons'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; +import { ApiController } from 'controllers/Api'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const Forms = forwardRef( ( @@ -32,8 +32,8 @@ export const Forms = forwardRef( ref: ForwardedRef ) => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { newBatchCall } = useBatchCall(); @@ -59,7 +59,8 @@ export const Forms = forwardRef( ) || 0; const getCalls = () => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return []; } @@ -69,7 +70,13 @@ export const Forms = forwardRef( return []; } return paginatedValidators.forEach(([page, v]) => - calls.push(api.tx.staking.payoutStakersByPage(v, era, page)) + calls.push( + pApi.tx.Staking.payout_stakers_by_page({ + validator_stash: v, + era: Number(era), + page, + }) + ) ); }); return calls; @@ -89,7 +96,7 @@ export const Forms = forwardRef( const getTx = () => { const tx = null; const calls = getCalls(); - if (!valid || !api || !calls.length) { + if (!valid || !calls.length) { return tx; } diff --git a/packages/app/src/modals/ClaimReward/index.tsx b/packages/app/src/modals/ClaimReward/index.tsx index 7c8ec608c..c59eceaa4 100644 --- a/packages/app/src/modals/ClaimReward/index.tsx +++ b/packages/app/src/modals/ClaimReward/index.tsx @@ -3,11 +3,9 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; @@ -18,23 +16,24 @@ import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; import { planckToUnit } from '@w3ux/utils'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const ClaimReward = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); - const { notEnoughFunds } = useTxMeta(); - const { activeAccount } = useActiveAccounts(); - const { getSignerWarnings } = useSignerWarnings(); - const { activePool, pendingPoolRewards } = useActivePool(); const { setModalStatus, config: { options }, setModalResize, } = useOverlay().modal; - + const { notEnoughFunds } = useTxMeta(); + const { activeAccount } = useActiveAccounts(); + const { getSignerWarnings } = useSignerWarnings(); + const { activePool, pendingPoolRewards } = useActivePool(); const { claimType } = options; // ensure selected payout is valid @@ -51,15 +50,16 @@ export const ClaimReward = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api) { + if (!pApi) { return tx; } if (claimType === 'bond') { - tx = api.tx.nominationPools.bondExtra('Rewards'); + tx = pApi.tx.NominationPools.bond_extra({ extra: { type: 'Rewards' } }); } else { - tx = api.tx.nominationPools.claimPayout(); + tx = pApi.tx.NominationPools.claim_payout(); } return tx; }; diff --git a/packages/app/src/modals/ManageFastUnstake/index.tsx b/packages/app/src/modals/ManageFastUnstake/index.tsx index 427e7ff7b..6461af8ee 100644 --- a/packages/app/src/modals/ManageFastUnstake/index.tsx +++ b/packages/app/src/modals/ManageFastUnstake/index.tsx @@ -10,7 +10,6 @@ import { useFastUnstake } from 'contexts/FastUnstake'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { useUnstaking } from 'hooks/useUnstaking'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; @@ -23,22 +22,24 @@ import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; import { ModalNotes } from 'kits/Overlay/structure/ModalNotes'; import { planckToUnitBn } from 'library/Utils'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const ManageFastUnstake = () => { const { t } = useTranslation('modals'); const { - api, consts: { bondDuration, fastUnstakeDeposit }, networkMetrics: { fastUnstakeErasToCheckPerBlock }, activeEra, } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); const { isFastUnstaking } = useUnstaking(); + const { activeAccount } = useActiveAccounts(); const { setModalResize, setModalStatus } = useOverlay().modal; const { getSignerWarnings } = useSignerWarnings(); const { feeReserve, getTransferOptions } = useTransferOptions(); @@ -82,14 +83,15 @@ export const ManageFastUnstake = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } if (!isFastUnstaking) { - tx = api.tx.fastUnstake.registerFastUnstake(); + tx = pApi.tx.FastUnstake.register_fast_unstake(); } else { - tx = api.tx.fastUnstake.deregister(); + tx = pApi.tx.FastUnstake.deregister(); } return tx; }; diff --git a/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx b/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx index 94f98eb6b..164328636 100644 --- a/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/ClaimCommission/index.tsx @@ -7,11 +7,9 @@ import BigNumber from 'bignumber.js'; import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from 'kits/Overlay/Provider'; import { useNetwork } from 'contexts/Network'; @@ -22,6 +20,8 @@ import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; import { ModalNotes } from 'kits/Overlay/structure/ModalNotes'; import { planckToUnitBn } from 'library/Utils'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const ClaimCommission = ({ setSection, @@ -29,8 +29,8 @@ export const ClaimCommission = ({ setSection: Dispatch>; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { setModalStatus } = useOverlay().modal; @@ -52,10 +52,11 @@ export const ClaimCommission = ({ // tx to submit const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi || poolId === undefined) { return null; } - return api.tx.nominationPools.claimCommission(poolId); + return pApi.tx.NominationPools.claim_commission({ pool_id: poolId }); }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx b/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx index 468189a32..0f7c2a087 100644 --- a/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/LeavePool/index.tsx @@ -13,7 +13,6 @@ import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { timeleftAsString, planckToUnitBn } from 'library/Utils'; import { SubmitTx } from 'library/SubmitTx'; import { StaticNote } from 'modals/Utils/StaticNote'; @@ -24,6 +23,8 @@ import { ButtonSubmitInvert } from 'ui-buttons'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const LeavePool = ({ setSection, @@ -31,8 +32,9 @@ export const LeavePool = ({ setSection: Dispatch>; }) => { const { t } = useTranslation('modals'); - const { api, consts } = useApi(); + const { consts } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { erasToSeconds } = useErasToTimeLeft(); @@ -80,8 +82,9 @@ export const LeavePool = ({ // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } @@ -90,7 +93,13 @@ export const LeavePool = ({ units ).toString(); - tx = api.tx.nominationPools.unbond(activeAccount, bondToSubmit); + tx = pApi.tx.NominationPools.unbond({ + member_account: { + type: 'Id', + value: activeAccount, + }, + unbonding_points: BigInt(bondToSubmit), + }); return tx; }; diff --git a/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx b/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx index 5fca29e24..97aedbec7 100644 --- a/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/ManageCommission/index.tsx @@ -11,9 +11,7 @@ import { useHelp } from 'contexts/Help'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; -import { useBatchCall } from 'hooks/useBatchCall'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import 'rc-slider/assets/index.css'; import { useOverlay } from 'kits/Overlay/Provider'; @@ -26,6 +24,10 @@ import { ButtonHelp, ButtonSubmitInvert } from 'ui-buttons'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useBatchCall } from 'hooks/useBatchCall'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const ManageCommission = ({ setSection, @@ -37,15 +39,8 @@ export const ManageCommission = ({ const { t } = useTranslation('modals'); const { openHelp } = useHelp(); const { - api, poolsConfig: { globalMaxCommission }, } = useApi(); - const { newBatchCall } = useBatchCall(); - const { activeAccount } = useActiveAccounts(); - const { setModalStatus } = useOverlay().modal; - const { isOwner, activePool } = useActivePool(); - const { getSignerWarnings } = useSignerWarnings(); - const { getBondedPool, updateBondedPools } = useBondedPools(); const { getInitial, getCurrent, @@ -55,6 +50,13 @@ export const ManageCommission = ({ resetAll, isUpdated, } = usePoolCommission(); + const { network } = useNetwork(); + const { newBatchCall } = useBatchCall(); + const { activeAccount } = useActiveAccounts(); + const { setModalStatus } = useOverlay().modal; + const { isOwner, activePool } = useActivePool(); + const { getSignerWarnings } = useSignerWarnings(); + const { getBondedPool, updateBondedPools } = useBondedPools(); const poolId = activePool?.id || 0; const bondedPool = getBondedPool(poolId); @@ -125,39 +127,45 @@ export const ManageCommission = ({ // tx to submit. const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi) { return null; } const txs = []; if (commissionUpdated) { txs.push( - api.tx.nominationPools.setCommission( - poolId, - currentCommissionSet + pApi.tx.NominationPools.set_commission({ + pool_id: poolId, + new_commission: currentCommissionSet ? [ - new BigNumber(commission).multipliedBy(10000000).toString(), + new BigNumber(commission).multipliedBy(10000000).toNumber(), payee, ] - : null - ) + : undefined, + }) ); } if (isUpdated('max_commission') && getEnabled('max_commission')) { txs.push( - api.tx.nominationPools.setCommissionMax( - poolId, - new BigNumber(maxCommission).multipliedBy(10000000).toString() - ) + pApi.tx.NominationPools.set_commission_max({ + pool_id: poolId, + max_commission: new BigNumber(maxCommission) + .multipliedBy(10000000) + .toNumber(), + }) ); } if (isUpdated('change_rate') && getEnabled('change_rate')) { txs.push( - api.tx.nominationPools.setCommissionChangeRate(poolId, { - maxIncrease: new BigNumber(changeRate.maxIncrease) - .multipliedBy(10000000) - .toString(), - minDelay: changeRate.minDelay.toString(), + pApi.tx.NominationPools.set_commission_change_rate({ + pool_id: poolId, + change_rate: { + max_increase: new BigNumber(changeRate.maxIncrease) + .multipliedBy(10000000) + .toNumber(), + min_delay: changeRate.minDelay.toNumber(), + }, }) ); } diff --git a/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx b/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx index c9db0c8a5..ddd2f11d0 100644 --- a/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx @@ -6,18 +6,20 @@ import { u8aUnwrapBytes, u8aToString } from '@polkadot/util'; import type { Dispatch, FormEvent, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from 'kits/Overlay/Provider'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { ButtonSubmitInvert } from 'ui-buttons'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { Binary } from 'polkadot-api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const RenamePool = ({ setSection, @@ -27,7 +29,7 @@ export const RenamePool = ({ section: number; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { setModalStatus } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); const { isOwner, activePool } = useActivePool(); @@ -58,10 +60,14 @@ export const RenamePool = ({ // tx to submit const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi) { return null; } - return api.tx.nominationPools.setMetadata(poolId, metadata); + return pApi.tx.NominationPools.set_metadata({ + pool_id: poolId, + metadata: Binary.fromText(metadata), + }); }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx b/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx index 530119ffe..aebd963b3 100644 --- a/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx @@ -5,12 +5,10 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { ClaimPermissionInput } from 'library/Form/ClaimPermissionInput'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from 'kits/Overlay/Provider'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -20,6 +18,9 @@ import { ButtonSubmitInvert } from 'ui-buttons'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { defaultClaimPermission } from 'controllers/ActivePools/defaults'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const SetClaimPermission = ({ setSection, @@ -29,7 +30,7 @@ export const SetClaimPermission = ({ setSection: Dispatch>; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { getPoolMembership } = useBalances(); const { activeAccount } = useActiveAccounts(); const { setModalStatus } = useOverlay().modal; @@ -60,10 +61,15 @@ export const SetClaimPermission = ({ // tx to submit. const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi) { return null; } - return api.tx.nominationPools.setClaimPermission(claimPermission); + return pApi.tx.NominationPools.set_claim_permission({ + permission: { + type: claimPermission, + }, + }); }; const submitExtrinsic = useSubmitExtrinsic({ diff --git a/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx b/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx index 302cd21d4..5b0e83129 100644 --- a/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx @@ -5,12 +5,10 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { useBondedPools } from 'contexts/Pools/BondedPools'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from 'kits/Overlay/Provider'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; @@ -18,6 +16,9 @@ import { ButtonSubmitInvert } from 'ui-buttons'; import { ActionItem } from 'library/ActionItem'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const SetPoolState = ({ setSection, @@ -27,7 +28,7 @@ export const SetPoolState = ({ task?: string; }) => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { setModalStatus } = useOverlay().modal; const { activeAccount } = useActiveAccounts(); const { getSignerWarnings } = useSignerWarnings(); @@ -80,20 +81,30 @@ export const SetPoolState = ({ // tx to submit const getTx = () => { - if (!valid || !api) { + const { pApi } = ApiController.get(network); + if (!valid || !pApi || poolId === undefined) { return null; } let tx; switch (task) { case 'destroy_pool': - tx = api.tx.nominationPools.setState(poolId, 'Destroying'); + tx = pApi.tx.NominationPools.set_state({ + pool_id: poolId, + state: { type: 'Destroying' }, + }); break; case 'unlock_pool': - tx = api.tx.nominationPools.setState(poolId, 'Open'); + tx = pApi.tx.NominationPools.set_state({ + pool_id: poolId, + state: { type: 'Open' }, + }); break; case 'lock_pool': - tx = api.tx.nominationPools.setState(poolId, 'Blocked'); + tx = pApi.tx.NominationPools.set_state({ + pool_id: poolId, + state: { type: 'Blocked' }, + }); break; default: tx = null; diff --git a/packages/app/src/modals/StopNominations/index.tsx b/packages/app/src/modals/StopNominations/index.tsx index 486783c9e..918404ade 100644 --- a/packages/app/src/modals/StopNominations/index.tsx +++ b/packages/app/src/modals/StopNominations/index.tsx @@ -3,12 +3,10 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import { useActivePool } from 'contexts/Pools/ActivePool'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; @@ -18,10 +16,13 @@ import { useBalances } from 'contexts/Balances'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ModalSeparator } from 'kits/Overlay/structure/ModalSeparator'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const StopNominations = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); const { getNominations } = useBalances(); @@ -62,16 +63,17 @@ export const StopNominations = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } if (isPool) { // wishing to stop all nominations, call chill - tx = api.tx.nominationPools.chill(activePool?.id || 0); + tx = pApi.tx.NominationPools.chill({ pool_id: activePool?.id || 0 }); } else if (isStaking) { - tx = api.tx.staking.chill(); + tx = pApi.tx.Staking.chill(); } return tx; }; diff --git a/packages/app/src/modals/Unbond/index.tsx b/packages/app/src/modals/Unbond/index.tsx index 28f7c3358..0753c2665 100644 --- a/packages/app/src/modals/Unbond/index.tsx +++ b/packages/app/src/modals/Unbond/index.tsx @@ -15,7 +15,6 @@ import { UnbondFeedback } from 'library/Form/Unbond/UnbondFeedback'; import { Warning } from 'library/Form/Warning'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { timeleftAsString, planckToUnitBn } from 'library/Utils'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; @@ -26,6 +25,8 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ModalNotes } from 'kits/Overlay/structure/ModalNotes'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const Unbond = () => { const { t } = useTranslation('modals'); @@ -34,6 +35,7 @@ export const Unbond = () => { const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); const { + network, networkData: { units, unit }, } = useNetwork(); const { erasToSeconds } = useErasToTimeLeft(); @@ -47,7 +49,6 @@ export const Unbond = () => { config: { options }, } = useOverlay().modal; const { - api, consts, poolsConfig: { minJoinBond: minJoinBondBn, minCreateBond: minCreateBondBn }, } = useApi(); @@ -104,8 +105,9 @@ export const Unbond = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } @@ -116,9 +118,12 @@ export const Unbond = () => { // determine tx if (isPooling) { - tx = api.tx.nominationPools.unbond(activeAccount, bondToSubmit); + tx = pApi.tx.NominationPools.unbond({ + member_account: { type: 'Id', value: activeAccount }, + unbonding_points: BigInt(bondToSubmit), + }); } else if (isStaking) { - tx = api.tx.staking.unbond(bondToSubmit); + tx = pApi.tx.Staking.unbond({ value: BigInt(bondToSubmit) }); } return tx; }; diff --git a/packages/app/src/modals/UnlockChunks/Forms.tsx b/packages/app/src/modals/UnlockChunks/Forms.tsx index 2d5c216d1..869617ef6 100644 --- a/packages/app/src/modals/UnlockChunks/Forms.tsx +++ b/packages/app/src/modals/UnlockChunks/Forms.tsx @@ -15,7 +15,6 @@ import { usePoolMembers } from 'contexts/Pools/PoolMembers'; import { useFavoritePools } from 'contexts/Pools/FavoritePools'; import { Warning } from 'library/Form/Warning'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { SubmitTx } from 'library/SubmitTx'; import { useOverlay } from 'kits/Overlay/Provider'; import { useNetwork } from 'contexts/Network'; @@ -28,6 +27,8 @@ import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; import { planckToUnitBn } from 'library/Utils'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const Forms = forwardRef( ( @@ -35,8 +36,9 @@ export const Forms = forwardRef( ref: ForwardedRef ) => { const { t } = useTranslation('modals'); - const { api, consts } = useApi(); + const { consts } = useApi(); const { + network, networkData: { units, unit }, } = useNetwork(); const { activePool } = useActivePool(); @@ -67,20 +69,28 @@ export const Forms = forwardRef( // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api || !unlock) { + if (!valid || !pApi || !unlock) { return tx; } // rebond is only available when staking directly. if (task === 'rebond' && isStaking) { - tx = api.tx.staking.rebond(unlock.value.toNumber() || 0); + tx = pApi.tx.Staking.rebond({ + value: BigInt(unlock.value.toNumber() || 0), + }); } else if (task === 'withdraw' && isStaking) { - tx = api.tx.staking.withdrawUnbonded(historyDepth.toString()); + tx = pApi.tx.Staking.withdraw_unbonded({ + num_slashing_spans: historyDepth.toNumber(), + }); } else if (task === 'withdraw' && isPooling && activePool) { - tx = api.tx.nominationPools.withdrawUnbonded( - activeAccount, - historyDepth.toString() - ); + tx = pApi.tx.NominationPools.withdraw_unbonded({ + member_account: { + type: 'Id', + value: activeAccount, + }, + num_slashing_spans: historyDepth.toNumber(), + }); } return tx; }; diff --git a/packages/app/src/modals/Unstake/index.tsx b/packages/app/src/modals/Unstake/index.tsx index 4e113fbb2..52d99b812 100644 --- a/packages/app/src/modals/Unstake/index.tsx +++ b/packages/app/src/modals/Unstake/index.tsx @@ -9,10 +9,8 @@ import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import { useTransferOptions } from 'contexts/TransferOptions'; import { Warning } from 'library/Form/Warning'; -import { useBatchCall } from 'hooks/useBatchCall'; import { useErasToTimeLeft } from 'hooks/useErasToTimeLeft'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { timeleftAsString, planckToUnitBn } from 'library/Utils'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; @@ -25,13 +23,17 @@ import { useBalances } from 'contexts/Balances'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; import { ActionItem } from 'library/ActionItem'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import { ApiController } from 'controllers/Api'; +import { useBatchCall } from 'hooks/useBatchCall'; export const Unstake = () => { const { t } = useTranslation('modals'); const { + network, networkData: { units, unit }, } = useNetwork(); - const { api, consts } = useApi(); + const { consts } = useApi(); const { notEnoughFunds } = useTxMeta(); const { newBatchCall } = useBatchCall(); const { getBondedAccount } = useBonded(); @@ -80,8 +82,9 @@ export const Unstake = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); const tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } // remove decimal errors @@ -91,11 +94,11 @@ export const Unstake = () => { ); if (bondToSubmit == 0n) { - return api.tx.staking.chill(); + return pApi.tx.Staking.chill(); } const txs = [ - api.tx.staking.chill(), - api.tx.staking.unbond(bondToSubmit.toString()), + pApi.tx.Staking.chill(), + pApi.tx.Staking.unbond({ value: BigInt(bondToSubmit.toString()) }), ]; return newBatchCall(txs, controller); }; diff --git a/packages/app/src/modals/UpdatePayee/index.tsx b/packages/app/src/modals/UpdatePayee/index.tsx index f090e30e5..2d70855e6 100644 --- a/packages/app/src/modals/UpdatePayee/index.tsx +++ b/packages/app/src/modals/UpdatePayee/index.tsx @@ -4,13 +4,11 @@ import { isValidAddress } from '@w3ux/utils'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useBonded } from 'contexts/Bonded'; import type { PayeeConfig, PayeeOptions } from 'contexts/Setup/types'; import { Warning } from 'library/Form/Warning'; import { usePayeeConfig } from 'hooks/usePayeeConfig'; import { useSignerWarnings } from 'hooks/useSignerWarnings'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Title } from 'library/Modal/Title'; import { PayeeInput } from 'library/PayeeInput'; import { SelectItems } from 'library/SelectItems'; @@ -23,10 +21,13 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { useBalances } from 'contexts/Balances'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; import { ModalWarnings } from 'kits/Overlay/structure/ModalWarnings'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const UpdatePayee = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); + const { network } = useNetwork(); const { getPayee } = useBalances(); const { notEnoughFunds } = useTxMeta(); const { getBondedAccount } = useBonded(); @@ -72,20 +73,21 @@ export const UpdatePayee = () => { // Tx to submit. const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; - - if (!api) { + if (!pApi) { return tx; } const payeeToSubmit = !isComplete() - ? 'Staked' + ? { type: 'Staked' } : selected.destination === 'Account' ? { - Account: selected.account, + type: 'Account', + value: selected.account, } - : selected.destination; + : { type: selected.destination }; - tx = api.tx.staking.setPayee(payeeToSubmit); + tx = pApi.tx.Staking.set_payee({ payee: payeeToSubmit }); return tx; }; From bbdec4b396cf623865959aa5d0c5e4b012ad4584 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 11:46:35 +0700 Subject: [PATCH 59/84] migrate remaining tx, stop exposing pjs api --- .../src/canvas/CreatePool/Summary/index.tsx | 2 +- packages/app/src/contexts/Api/defaults.ts | 1 - packages/app/src/contexts/Api/index.tsx | 1 - packages/app/src/contexts/Api/types.ts | 1 - .../src/contexts/Pools/ActivePool/index.tsx | 4 +-- .../src/contexts/Pools/JoinPools/index.tsx | 4 +-- .../contexts/Pools/PoolPerformance/index.tsx | 5 ++- packages/app/src/contexts/Staking/index.tsx | 10 +++--- .../app/src/modals/ChangePoolRoles/index.tsx | 34 +++++++++++++------ .../src/modals/ImportWalletConnect/index.tsx | 4 +-- 10 files changed, 37 insertions(+), 29 deletions(-) diff --git a/packages/app/src/canvas/CreatePool/Summary/index.tsx b/packages/app/src/canvas/CreatePool/Summary/index.tsx index 149cd7058..5af046668 100644 --- a/packages/app/src/canvas/CreatePool/Summary/index.tsx +++ b/packages/app/src/canvas/CreatePool/Summary/index.tsx @@ -81,7 +81,7 @@ export const Summary = ({ section }: SetupStepProps) => { }), pApi.tx.NominationPools.set_metadata({ pool_id: poolId.toNumber(), - metadata: Binary.fromHex(metadata), + metadata: Binary.fromText(metadata), }), ]; return newBatchCall(txs, activeAccount); diff --git a/packages/app/src/contexts/Api/defaults.ts b/packages/app/src/contexts/Api/defaults.ts index 6c03b014a..c1ea6362a 100644 --- a/packages/app/src/contexts/Api/defaults.ts +++ b/packages/app/src/contexts/Api/defaults.ts @@ -80,7 +80,6 @@ export const defaultStakingMetrics: APIStakingMetrics = { }; export const defaultApiContext: APIContextInterface = { - api: null, peopleApi: null, chainSpecs: defaultChainSpecs, isReady: false, diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 279eac3cd..65d516410 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -549,7 +549,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { return ( useContext(ActivePoolContext); export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { getPoolMembership } = useBalances(); - const { isReady, api, peopleApi } = useApi(); + const { isReady, peopleApi } = useApi(); const { activeAccount } = useActiveAccounts(); const createPoolAccounts = useCreatePoolAccounts(); @@ -71,7 +71,7 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { // Sync active pool subscriptions. const syncActivePoolSubscriptions = async () => { - if (api && accountPoolId) { + if (isReady && accountPoolId) { const newActivePool = [ { id: accountPoolId, diff --git a/packages/app/src/contexts/Pools/JoinPools/index.tsx b/packages/app/src/contexts/Pools/JoinPools/index.tsx index 150f4eca7..df7cfb742 100644 --- a/packages/app/src/contexts/Pools/JoinPools/index.tsx +++ b/packages/app/src/contexts/Pools/JoinPools/index.tsx @@ -22,7 +22,7 @@ export const useJoinPools = () => useContext(JoinPoolsContext); export const JoinPoolsProvider = ({ children }: { children: ReactNode }) => { const { - api, + isReady, activeEra, networkMetrics: { minimumActiveStake }, } = useApi(); @@ -45,7 +45,7 @@ export const JoinPoolsProvider = ({ children }: { children: ReactNode }) => { // Trigger worker to calculate join pool performance data. useEffectIgnoreInitial(() => { if ( - api && + isReady && bondedPools.length && activeEra.index.isGreaterThan(0) && erasRewardPointsFetched === 'synced' && diff --git a/packages/app/src/contexts/Pools/PoolPerformance/index.tsx b/packages/app/src/contexts/Pools/PoolPerformance/index.tsx index 3413570c7..1a09e1740 100644 --- a/packages/app/src/contexts/Pools/PoolPerformance/index.tsx +++ b/packages/app/src/contexts/Pools/PoolPerformance/index.tsx @@ -38,7 +38,7 @@ export const PoolPerformanceProvider = ({ children: ReactNode; }) => { const { network } = useNetwork(); - const { api, activeEra } = useApi(); + const { isReady, activeEra } = useApi(); const { getPagedErasStakers } = useStaking(); const { erasRewardPoints } = useValidators(); @@ -179,10 +179,9 @@ export const PoolPerformanceProvider = ({ // Get era data and send to worker. const processEra = async (key: PoolRewardPointsKey, era: BigNumber) => { - if (!api) { + if (!isReady) { return; } - // NOTE: This will not make any difference on the first run. updateTaskCurrentEra(key, era); diff --git a/packages/app/src/contexts/Staking/index.tsx b/packages/app/src/contexts/Staking/index.tsx index 1a84bd4c7..ea0cd3c49 100644 --- a/packages/app/src/contexts/Staking/index.tsx +++ b/packages/app/src/contexts/Staking/index.tsx @@ -41,7 +41,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { const { getBondedAccount } = useBonded(); const { networkData, network } = useNetwork(); const { getLedger, getNominations } = useBalances(); - const { isReady, api, activeEra, apiStatus } = useApi(); + const { isReady, activeEra, apiStatus } = useApi(); const { accounts: connectAccounts } = useImportedAccounts(); const { activeAccount, getActiveAccount } = useActiveAccounts(); @@ -93,7 +93,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // Fetches erasStakers exposures for an era, and saves to `localStorage`. const fetchEraStakers = async (era: string) => { - if (!isReady || activeEra.index.isZero() || !api) { + if (!isReady || activeEra.index.isZero()) { return []; } @@ -120,7 +120,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // Fetches the active nominator set and metadata around it. const fetchActiveEraStakers = async () => { - if (!isReady || activeEra.index.isZero() || !api) { + if (!isReady || activeEra.index.isZero()) { return; } @@ -224,11 +224,11 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { // Fetch eras stakers from storage. const getPagedErasStakers = async (era: string) => { - if (!api) { + const { pApi } = ApiController.get(network); + if (!pApi) { return []; } - const { pApi } = ApiController.get(network); const overview = await new ErasStakersOverview(pApi).fetch(era); const validators: Record = overview.reduce( ( diff --git a/packages/app/src/modals/ChangePoolRoles/index.tsx b/packages/app/src/modals/ChangePoolRoles/index.tsx index 8c652afe6..5fcb9aef7 100644 --- a/packages/app/src/modals/ChangePoolRoles/index.tsx +++ b/packages/app/src/modals/ChangePoolRoles/index.tsx @@ -2,9 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import { useTranslation } from 'react-i18next'; -import { useApi } from 'contexts/Api'; import { useBondedPools } from 'contexts/Pools/BondedPools'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; import { SubmitTx } from 'library/SubmitTx'; import { useTxMeta } from 'contexts/TxMeta'; @@ -14,13 +12,16 @@ import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { RoleChange } from './RoleChange'; import { Wrapper } from './Wrapper'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; +import { ApiController } from 'controllers/Api'; +import { useNetwork } from 'contexts/Network'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; export const ChangePoolRoles = () => { const { t } = useTranslation('modals'); - const { api } = useApi(); - const { activeAccount } = useActiveAccounts(); + const { network } = useNetwork(); const { notEnoughFunds } = useTxMeta(); const { replacePoolRoles } = useBondedPools(); + const { activeAccount } = useActiveAccounts(); const { setModalStatus, config: { options }, @@ -30,18 +31,29 @@ export const ChangePoolRoles = () => { // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); let tx = null; + if (!pApi) { + return tx; + } + + const removeOp = { type: 'Remove' }; const root = roleEdits?.root?.newAddress - ? { Set: roleEdits?.root?.newAddress } - : 'Remove'; + ? { type: 'Set', value: roleEdits.root.newAddress } + : removeOp; const nominator = roleEdits?.nominator?.newAddress - ? { Set: roleEdits?.nominator?.newAddress } - : 'Remove'; + ? { type: 'Set', value: roleEdits.nominator.newAddress } + : removeOp; const bouncer = roleEdits?.bouncer?.newAddress - ? { Set: roleEdits?.bouncer?.newAddress } - : 'Remove'; + ? { type: 'Set', value: roleEdits.bouncer.newAddress } + : removeOp; - tx = api?.tx.nominationPools?.updateRoles(poolId, root, nominator, bouncer); + tx = pApi.tx.NominationPools.update_roles({ + pool_id: poolId, + new_root: root, + new_nominator: nominator, + new_bouncer: bouncer, + }); return tx; }; diff --git a/packages/app/src/modals/ImportWalletConnect/index.tsx b/packages/app/src/modals/ImportWalletConnect/index.tsx index b1b2dff3d..05f513681 100644 --- a/packages/app/src/modals/ImportWalletConnect/index.tsx +++ b/packages/app/src/modals/ImportWalletConnect/index.tsx @@ -25,7 +25,7 @@ export const ImportWalletConnect = () => { const { t } = useTranslation(); const { addWcAccount, getWcAccounts, wcAccountExists, renameWcAccount } = useWcAccounts(); - const { api } = useApi(); + const { isReady } = useApi(); const { network } = useNetwork(); const { status: promptStatus } = usePrompt(); const { replaceModal, setModalResize } = useOverlay().modal; @@ -41,7 +41,7 @@ export const ImportWalletConnect = () => { // Handle wallet account importing. const handleImportAddresses = async () => { - if (!wcInitialized || !api) { + if (!wcInitialized || !isReady) { return; } From 88d011952fc768cc4e650f629e0af765c50deeba Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 11:48:11 +0700 Subject: [PATCH 60/84] fixes --- .../app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx | 4 ++-- packages/app/src/contexts/Balances/index.tsx | 4 ++-- packages/app/src/contexts/Connect/ImportedAccounts/index.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx b/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx index 715a280c8..98d829481 100644 --- a/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx +++ b/packages/app/src/canvas/PoolMembers/Prompts/WithdrawMember.tsx @@ -39,7 +39,7 @@ export const WithdrawMember = ({ networkData: { units, unit }, } = useNetwork(); const { closePrompt } = usePrompt(); - const { api, consts, activeEra } = useApi(); + const { consts, activeEra } = useApi(); const { activeAccount } = useActiveAccounts(); const { removePoolMember } = usePoolMembers(); const { getSignerWarnings } = useSignerWarnings(); @@ -69,7 +69,7 @@ export const WithdrawMember = ({ const getTx = () => { const { pApi } = ApiController.get(network); let tx = null; - if (!valid || !api) { + if (!valid || !pApi) { return tx; } tx = pApi.tx.NominationPools.withdraw_unbonded({ diff --git a/packages/app/src/contexts/Balances/index.tsx b/packages/app/src/contexts/Balances/index.tsx index 7564d4fea..92abd42a4 100644 --- a/packages/app/src/contexts/Balances/index.tsx +++ b/packages/app/src/contexts/Balances/index.tsx @@ -27,7 +27,7 @@ export const useBalances = () => useContext(BalancesContext); export const BalancesProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); - const { api, peopleApi } = useApi(); + const { peopleApi, isReady } = useApi(); const { getBondedAccount } = useBonded(); const { accounts } = useImportedAccounts(); const createPoolAccounts = useCreatePoolAccounts(); @@ -61,7 +61,7 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { // If a pool membership exists, let `ActivePools` know of pool membership to re-sync pool // details and nominations. - if (api && poolMembership) { + if (isReady && poolMembership) { const { poolId } = poolMembership; const newPools = ActivePoolsController.getformattedPoolItems( address diff --git a/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx b/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx index bac80eb17..7f7de3c98 100644 --- a/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx +++ b/packages/app/src/contexts/Connect/ImportedAccounts/index.tsx @@ -32,7 +32,7 @@ export const ImportedAccountsProvider = ({ }: { children: ReactNode; }) => { - const { isReady, api } = useApi(); + const { isReady } = useApi(); const { network, networkData: { ss58 }, @@ -115,7 +115,7 @@ export const ImportedAccountsProvider = ({ // Keep accounts in sync with `BalancesController`. useEffectIgnoreInitial(() => { - if (api && isReady) { + if (isReady) { BalancesController.syncAccounts( network, allAccounts.map((a) => a.address) From 50d9f08363755834f0d6944b649b3aed1ea26010 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 12:03:08 +0700 Subject: [PATCH 61/84] stop exposing `peopleApi`, combine ready events --- packages/app/src/contexts/Api/defaults.ts | 1 - packages/app/src/contexts/Api/index.tsx | 3 +- packages/app/src/contexts/Api/types.ts | 2 -- packages/app/src/contexts/Balances/index.tsx | 9 +++-- .../src/contexts/Pools/ActivePool/index.tsx | 7 +++- .../Validators/ValidatorEntries/index.tsx | 5 ++- packages/app/src/model/Api/index.ts | 33 +++---------------- 7 files changed, 21 insertions(+), 39 deletions(-) diff --git a/packages/app/src/contexts/Api/defaults.ts b/packages/app/src/contexts/Api/defaults.ts index c1ea6362a..a48c6668e 100644 --- a/packages/app/src/contexts/Api/defaults.ts +++ b/packages/app/src/contexts/Api/defaults.ts @@ -80,7 +80,6 @@ export const defaultStakingMetrics: APIStakingMetrics = { }; export const defaultApiContext: APIContextInterface = { - peopleApi: null, chainSpecs: defaultChainSpecs, isReady: false, apiStatus: 'disconnected', diff --git a/packages/app/src/contexts/Api/index.tsx b/packages/app/src/contexts/Api/index.tsx index 65d516410..552cb44c1 100644 --- a/packages/app/src/contexts/Api/index.tsx +++ b/packages/app/src/contexts/Api/index.tsx @@ -291,7 +291,7 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { } }; - // Handle `polkadot-api` events. + // Handle api status events. const handleNewApiStatus = (e: Event) => { if (isCustomEvent(e)) { const { chainType } = e.detail; @@ -549,7 +549,6 @@ export const APIProvider = ({ children, network }: APIProviderProps) => { return ( ( defaults.defaultBalancesContext @@ -26,8 +27,8 @@ export const BalancesContext = createContext( export const useBalances = () => useContext(BalancesContext); export const BalancesProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); - const { peopleApi, isReady } = useApi(); const { getBondedAccount } = useBonded(); const { accounts } = useImportedAccounts(); const createPoolAccounts = useCreatePoolAccounts(); @@ -69,6 +70,10 @@ export const BalancesProvider = ({ children }: { children: ReactNode }) => { id: String(poolId), addresses: { ...createPoolAccounts(Number(poolId)) }, }); + + const { pApi: peopleApi } = ApiController.get( + `people-${network}` as SystemChainId + ); if (peopleApi) { ActivePoolsController.syncPools(network, address, newPools); } diff --git a/packages/app/src/contexts/Pools/ActivePool/index.tsx b/packages/app/src/contexts/Pools/ActivePool/index.tsx index 913326aab..f5209bba7 100644 --- a/packages/app/src/contexts/Pools/ActivePool/index.tsx +++ b/packages/app/src/contexts/Pools/ActivePool/index.tsx @@ -17,6 +17,7 @@ import { SyncController } from 'controllers/Sync'; import { useActivePools } from 'hooks/useActivePools'; import BigNumber from 'bignumber.js'; import { ApiController } from 'controllers/Api'; +import type { SystemChainId } from 'types'; export const ActivePoolContext = createContext( defaultActivePoolContext @@ -25,9 +26,9 @@ export const ActivePoolContext = createContext( export const useActivePool = () => useContext(ActivePoolContext); export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { + const { isReady } = useApi(); const { network } = useNetwork(); const { getPoolMembership } = useBalances(); - const { isReady, peopleApi } = useApi(); const { activeAccount } = useActiveAccounts(); const createPoolAccounts = useCreatePoolAccounts(); @@ -80,6 +81,10 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { ]; SyncController.dispatch('active-pools', 'syncing'); + const { pApi: peopleApi } = ApiController.get( + `people-${network}` as SystemChainId + ); + if (peopleApi) { ActivePoolsController.syncPools(network, activeAccount, newActivePool); } diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index e3aea5fdd..e4fd84fde 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -52,7 +52,6 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { const { network } = useNetwork(); const { isReady, - peopleApi, peopleApiStatus, consts: { historyDepth }, networkMetrics: { earliestStoredSession }, @@ -324,13 +323,13 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { setAvgCommission(avg); // NOTE: validators are shuffled before committed to state. setValidators(shuffle(validatorEntries)); - const { pApi: peoplePapiApi } = ApiController.get( + const { pApi: peopleApi } = ApiController.get( `people-${network}` as SystemChainId ); if (peopleApi && peopleApiStatus === 'ready') { const addresses = validatorEntries.map(({ address }) => address); const { identities, supers } = await IdentitiesController.fetch( - peoplePapiApi, + peopleApi, addresses ); setValidatorIdentities(identities); diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index d65f6088f..b6f75f77a 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -104,11 +104,6 @@ export class Api { // Initialise Polkadot JS API. this.#api = new ApiPromise({ provider: this.#provider }); - // NOTE: Unlike Polkadot JS API, Papi client does not have an asynchronous initialization - // stage that leads to `isReady`. If using papi client, we can immediately attempt to fetch - // the chainSpec via the client.∑ - this.initApiEvents(); - // Wait for api to be ready. await this.#api.isReady; @@ -203,8 +198,8 @@ export class Api { transactionVersion, }; - // Dispatch 'papi-ready' event to let contexts populate constants. - this.dispatchPapiReadyEvent(); + // Dispatch ready eventd to let contexts populate constants. + this.dispatchReadyEvent(); } catch (e) { // TODO: Expand this when PJS API has been removed. Flag an error if there are any issues // bootstrapping chain spec. NOTE: This can happen when PAPI is the standalone connection @@ -234,35 +229,17 @@ export class Api { } }; - // Handler for dispatching `papi-ready` events. - dispatchPapiReadyEvent() { + // Handler for dispatching ready events. + dispatchReadyEvent() { const detail: PapiReadyEvent = { network: this.network, chainType: this.#chainType, ...this.#papiChainSpec, }; + this.dispatchEvent(this.ensureEventStatus('ready')); document.dispatchEvent(new CustomEvent('papi-ready', { detail })); } - // Set up API event listeners. Sends information to the UI. - async initApiEvents() { - this.#api.on('ready', async () => { - this.dispatchEvent(this.ensureEventStatus('ready')); - }); - - this.#api.on('connected', () => { - this.dispatchEvent(this.ensureEventStatus('connected')); - }); - - this.#api.on('disconnected', () => { - this.dispatchEvent(this.ensureEventStatus('disconnected')); - }); - - this.#api.on('error', () => { - this.dispatchEvent(this.ensureEventStatus('error')); - }); - } - // Handler for dispatching `api-status` events. dispatchEvent( status: EventApiStatus, From c1f2f5890f655a2435b0126337eb69516c5d933e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 12:07:48 +0700 Subject: [PATCH 62/84] stop connecting to pjs --- .../app/src/controllers/ActivePools/types.ts | 3 -- packages/app/src/model/Api/index.ts | 46 +------------------ 2 files changed, 2 insertions(+), 47 deletions(-) diff --git a/packages/app/src/controllers/ActivePools/types.ts b/packages/app/src/controllers/ActivePools/types.ts index b838646fa..2753a60ad 100644 --- a/packages/app/src/controllers/ActivePools/types.ts +++ b/packages/app/src/controllers/ActivePools/types.ts @@ -1,7 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { VoidFn } from '@polkadot/api/types'; import type { Nominations } from 'contexts/Balances/types'; import type { ActivePool } from 'contexts/Pools/ActivePool/types'; @@ -22,5 +21,3 @@ export interface ActivePoolItem { export type AccountActivePools = Record; export type AccountPoolNominations = Record; - -export type AccountUnsubs = Record; diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index b6f75f77a..d4a69fe68 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -1,8 +1,7 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import { ApiPromise, WsProvider } from '@polkadot/api'; -import type { AnyApi, NetworkName, SystemChainId } from 'types'; +import type { NetworkName, SystemChainId } from 'types'; import { NetworkList, SystemChainList } from 'config/networks'; import type { ApiChainType, @@ -14,9 +13,6 @@ import type { PapiReadyEvent, } from './types'; import { SubscriptionsController } from 'controllers/Subscriptions'; -import { ScProvider } from '@polkadot/rpc-provider/substrate-connect'; -import { WellKnownChain } from '@substrate/connect'; -import * as Sc from '@substrate/connect'; import type { PolkadotClient } from 'polkadot-api'; import { createClient } from 'polkadot-api'; import { getWsProvider } from 'polkadot-api/ws-provider/web'; @@ -32,9 +28,6 @@ export class Api { // The type of chain being connected to. #chainType: ApiChainType; - // API provider. - #provider: WsProvider | ScProvider; - // PAPI Instance. #papiClient: PolkadotClient; @@ -44,19 +37,12 @@ export class Api { // PAPI Chain Spec. #papiChainSpec: PapiChainSpec; - // API instance. - #api: ApiPromise; - // The current RPC endpoint. #rpcEndpoint: string; // The current connection type. #connectionType: ConnectionType; - get api() { - return this.#api; - } - get papiClient() { return this.#papiClient; } @@ -101,12 +87,6 @@ export class Api { // Tell UI api is connecting. this.dispatchEvent(this.ensureEventStatus('connecting')); - // Initialise Polkadot JS API. - this.#api = new ApiPromise({ provider: this.#provider }); - - // Wait for api to be ready. - await this.#api.isReady; - // Initialise PAPI API. this.#pApi = this.#papiClient.getUnsafeApi(); @@ -128,27 +108,12 @@ export class Api { this.#rpcEndpoint ]; - // Initialize Polkadot JS Provider. - this.#provider = new WsProvider(endpoint); - - // Initialize PAPI Client. + // Initialize Polkadot API Client. this.#papiClient = createClient(getWsProvider(endpoint)); } // Dynamically load and connect to Substrate Connect. async initScProvider() { - // Get light client key from network list. - const lightClientKey = - this.#chainType === 'relay' - ? NetworkList[this.network].endpoints.lightClientKey - : SystemChainList[this.network].endpoints.lightClientKey; - - // Instantiate light client provider. - this.#provider = new ScProvider( - Sc as AnyApi, - WellKnownChain[lightClientKey as keyof typeof WellKnownChain] - ); - // Initialise PAPI light client. const smoldot = startFromWorker(new SmWorker()); const smMetadata = getLightClientMetadata(this.#chainType, this.network); @@ -162,9 +127,6 @@ export class Api { }) ); this.#papiClient = createClient(chain); - - // Connect to Polkadot JS API provider. - await this.#provider.connect(); } async fetchChainSpec() { @@ -291,10 +253,6 @@ export class Api { async disconnect(destroy = false) { this.unsubscribe(); - // Disconnect provider and api. - await this.#provider?.disconnect(); - await this.#api?.disconnect(); - // Disconnect from PAPI Client. this.#papiClient?.destroy(); From 7968cd864feca7c2c57d1ff2b947be9a7dc2f916 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 12:09:34 +0700 Subject: [PATCH 63/84] uninstall pjs --- packages/app/package.json | 2 - yarn.lock | 214 ++------------------------------------ 2 files changed, 8 insertions(+), 208 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 2e68e823b..32303b36c 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -26,8 +26,6 @@ "@polkadot-api/merkleize-metadata": "^1.1.4", "@polkadot-api/signers-common": "^0.1.1", "@polkadot-api/substrate-bindings": "^0.9.3", - "@polkadot/api": "^14.3.1", - "@polkadot/rpc-provider": "^14.3.1", "@polkawatch/ddp-client": "^2.0.20", "@substrate/connect": "^2.0.1", "@w3ux/extension-assets": "^1.0.0-beta.1", diff --git a/yarn.lock b/yarn.lock index 237b8fc47..05b7687aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2114,21 +2114,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-augment@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/api-augment@npm:14.3.1" - dependencies: - "@polkadot/api-base": "npm:14.3.1" - "@polkadot/rpc-augment": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-augment": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/2cb756c6b2d2e9249ef8706725ee83644e2bde323a916ade1976a653521c2680bcffef25455aeb58627ac48492b99abb89a0aeac75cfa26837fda5dde4011548 - languageName: node - linkType: hard - "@polkadot/api-base@npm:12.4.2": version: 12.4.2 resolution: "@polkadot/api-base@npm:12.4.2" @@ -2142,19 +2127,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-base@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/api-base@npm:14.3.1" - dependencies: - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/8e04af99ce4594fbbf934706ff3b77a54da9c13feaa5563c50de4add2c3f6763164a558fa123af26a9d208ca5689eeedec2574b5cd12a9d83cf958f21571212f - languageName: node - linkType: hard - "@polkadot/api-derive@npm:12.4.2": version: 12.4.2 resolution: "@polkadot/api-derive@npm:12.4.2" @@ -2173,24 +2145,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api-derive@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/api-derive@npm:14.3.1" - dependencies: - "@polkadot/api": "npm:14.3.1" - "@polkadot/api-augment": "npm:14.3.1" - "@polkadot/api-base": "npm:14.3.1" - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/7939b7580dfe4469b72f1d2b006ab6a3158ab65c779b4e42e94e3d7b449b04bad0f995b18bc4dbb28f2d03e64762a93ad6391faa3c2809ae0993d0973bdd9dc3 - languageName: node - linkType: hard - "@polkadot/api@npm:12.4.2, @polkadot/api@npm:^12.0.2": version: 12.4.2 resolution: "@polkadot/api@npm:12.4.2" @@ -2216,31 +2170,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/api@npm:14.3.1, @polkadot/api@npm:^14.3.1": - version: 14.3.1 - resolution: "@polkadot/api@npm:14.3.1" - dependencies: - "@polkadot/api-augment": "npm:14.3.1" - "@polkadot/api-base": "npm:14.3.1" - "@polkadot/api-derive": "npm:14.3.1" - "@polkadot/keyring": "npm:^13.2.3" - "@polkadot/rpc-augment": "npm:14.3.1" - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/rpc-provider": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-augment": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/types-create": "npm:14.3.1" - "@polkadot/types-known": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" - eventemitter3: "npm:^5.0.1" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/5c893cef918e9914c833c03b5f9fea86673cd2e50f8bdccb913177119c83124b036d69cdc8f52fd8d999b43f2da26a5260a8812cc516cba21b3fa727bd5e8023 - languageName: node - linkType: hard - "@polkadot/extension-inject@npm:0.48.2": version: 0.48.2 resolution: "@polkadot/extension-inject@npm:0.48.2" @@ -2273,7 +2202,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/keyring@npm:^13.0.2, @polkadot/keyring@npm:^13.2.3": +"@polkadot/keyring@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/keyring@npm:13.2.3" dependencies: @@ -2298,7 +2227,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/networks@npm:13.2.3, @polkadot/networks@npm:^13.0.2, @polkadot/networks@npm:^13.2.3": +"@polkadot/networks@npm:13.2.3, @polkadot/networks@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/networks@npm:13.2.3" dependencies: @@ -2322,19 +2251,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-augment@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/rpc-augment@npm:14.3.1" - dependencies: - "@polkadot/rpc-core": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/9a55690656c3e8154745439e1bc7f0e9d29af19d20374724bc9f21400540efe6f5d94bb18faf5336061e16f86afdeecf0d5f3e7b0ccfad5b87f602c5633d4743 - languageName: node - linkType: hard - "@polkadot/rpc-core@npm:12.4.2": version: 12.4.2 resolution: "@polkadot/rpc-core@npm:12.4.2" @@ -2349,20 +2265,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-core@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/rpc-core@npm:14.3.1" - dependencies: - "@polkadot/rpc-augment": "npm:14.3.1" - "@polkadot/rpc-provider": "npm:14.3.1" - "@polkadot/types": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/439223156cb243e5907655057367178562cdc4a30f960d67a61705b638cc017814055e0086a28e4b06b9b88e382302a7c17b1020b5a09f29dc37233ed2d7ffde - languageName: node - linkType: hard - "@polkadot/rpc-provider@npm:12.4.2, @polkadot/rpc-provider@npm:^12.0.2": version: 12.4.2 resolution: "@polkadot/rpc-provider@npm:12.4.2" @@ -2387,30 +2289,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/rpc-provider@npm:14.3.1, @polkadot/rpc-provider@npm:^14.3.1": - version: 14.3.1 - resolution: "@polkadot/rpc-provider@npm:14.3.1" - dependencies: - "@polkadot/keyring": "npm:^13.2.3" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-support": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" - "@polkadot/x-fetch": "npm:^13.2.3" - "@polkadot/x-global": "npm:^13.2.3" - "@polkadot/x-ws": "npm:^13.2.3" - "@substrate/connect": "npm:0.8.11" - eventemitter3: "npm:^5.0.1" - mock-socket: "npm:^9.3.1" - nock: "npm:^13.5.5" - tslib: "npm:^2.8.0" - dependenciesMeta: - "@substrate/connect": - optional: true - checksum: 10c0/cab0c873be695678f4e2086dd0e7ba3e79e3aa20f447f941a60f160d3a8f52bfa340c54b10e0e0cd8bdcdd57d8effcab230d31b9bec1e6432aa92b19e881f41c - languageName: node - linkType: hard - "@polkadot/types-augment@npm:12.1.1": version: 12.1.1 resolution: "@polkadot/types-augment@npm:12.1.1" @@ -2435,18 +2313,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-augment@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-augment@npm:14.3.1" - dependencies: - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/56192ce0bd656343196c46cea114ddf61488307ac8e4c7d578f6e41b877fc258765f1408ebe408ca9a0f7f64b16f661776fd76bb215f2d74c9690e4d4855de72 - languageName: node - linkType: hard - "@polkadot/types-codec@npm:12.1.1": version: 12.1.1 resolution: "@polkadot/types-codec@npm:12.1.1" @@ -2469,17 +2335,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-codec@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-codec@npm:14.3.1" - dependencies: - "@polkadot/util": "npm:^13.2.3" - "@polkadot/x-bigint": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/e60353aa1218b460167d97447a7edbab527cceeb505e8206c065890622db5e95c6b1e703c69d75fd2dad410c8c598b0f721995f5d1af357420b461038526641b - languageName: node - linkType: hard - "@polkadot/types-create@npm:12.1.1": version: 12.1.1 resolution: "@polkadot/types-create@npm:12.1.1" @@ -2502,17 +2357,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-create@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-create@npm:14.3.1" - dependencies: - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/18b07ebe2140010426f9c9c6648bd4e56911fc52465356c89b9969572db1c97a2f80bea985eb6f90d07bf1ca38b690050e36dc725ec5fcf4edeef85eabbe381a - languageName: node - linkType: hard - "@polkadot/types-known@npm:12.4.2": version: 12.4.2 resolution: "@polkadot/types-known@npm:12.4.2" @@ -2527,20 +2371,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-known@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-known@npm:14.3.1" - dependencies: - "@polkadot/networks": "npm:^13.2.3" - "@polkadot/types": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/types-create": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/c290b290b4856c77b15bc29a177caeaa6ce408b48c36a6c1fdf38160c5573a95bdd1476a1ad085dbe0853684deb4cd4c49fa67ee2d120809de8ec6e8005d08eb - languageName: node - linkType: hard - "@polkadot/types-support@npm:12.4.2": version: 12.4.2 resolution: "@polkadot/types-support@npm:12.4.2" @@ -2551,16 +2381,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types-support@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types-support@npm:14.3.1" - dependencies: - "@polkadot/util": "npm:^13.2.3" - tslib: "npm:^2.8.0" - checksum: 10c0/fec80615ee0cdfa9d70a9e73d598de5b58f01f76557ac9c0060f9036bac1dd3695184ce8e4a81461a83988da2c28e5a8248a941fc891b2606a681f1ef218adfa - languageName: node - linkType: hard - "@polkadot/types@npm:12.1.1": version: 12.1.1 resolution: "@polkadot/types@npm:12.1.1" @@ -2593,22 +2413,6 @@ __metadata: languageName: node linkType: hard -"@polkadot/types@npm:14.3.1": - version: 14.3.1 - resolution: "@polkadot/types@npm:14.3.1" - dependencies: - "@polkadot/keyring": "npm:^13.2.3" - "@polkadot/types-augment": "npm:14.3.1" - "@polkadot/types-codec": "npm:14.3.1" - "@polkadot/types-create": "npm:14.3.1" - "@polkadot/util": "npm:^13.2.3" - "@polkadot/util-crypto": "npm:^13.2.3" - rxjs: "npm:^7.8.1" - tslib: "npm:^2.8.0" - checksum: 10c0/1cf0ad0f5f51cce1f9f3a5a694669fe327e3b8ec2c277dd7f0be74c7df1147b8713651c3a99ad8ac9a8c125a7681c2d6d05b6a7b0eaaa96f96a52bd4c61a8a47 - languageName: node - linkType: hard - "@polkadot/util-crypto@npm:12.6.2, @polkadot/util-crypto@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/util-crypto@npm:12.6.2" @@ -2629,7 +2433,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/util-crypto@npm:13.2.3, @polkadot/util-crypto@npm:^13.0.2, @polkadot/util-crypto@npm:^13.2.3": +"@polkadot/util-crypto@npm:13.2.3, @polkadot/util-crypto@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/util-crypto@npm:13.2.3" dependencies: @@ -2769,7 +2573,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-bigint@npm:13.2.3, @polkadot/x-bigint@npm:^13.0.2, @polkadot/x-bigint@npm:^13.2.3": +"@polkadot/x-bigint@npm:13.2.3, @polkadot/x-bigint@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-bigint@npm:13.2.3" dependencies: @@ -2779,7 +2583,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-fetch@npm:^13.0.2, @polkadot/x-fetch@npm:^13.2.3": +"@polkadot/x-fetch@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-fetch@npm:13.2.3" dependencies: @@ -2799,7 +2603,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-global@npm:13.2.3, @polkadot/x-global@npm:^13.0.2, @polkadot/x-global@npm:^13.2.3": +"@polkadot/x-global@npm:13.2.3, @polkadot/x-global@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-global@npm:13.2.3" dependencies: @@ -2874,7 +2678,7 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-ws@npm:^13.0.2, @polkadot/x-ws@npm:^13.2.3": +"@polkadot/x-ws@npm:^13.0.2": version: 13.2.3 resolution: "@polkadot/x-ws@npm:13.2.3" dependencies: @@ -4784,8 +4588,6 @@ __metadata: "@polkadot-api/merkleize-metadata": "npm:^1.1.4" "@polkadot-api/signers-common": "npm:^0.1.1" "@polkadot-api/substrate-bindings": "npm:^0.9.3" - "@polkadot/api": "npm:^14.3.1" - "@polkadot/rpc-provider": "npm:^14.3.1" "@polkawatch/ddp-client": "npm:^2.0.20" "@substrate/connect": "npm:^2.0.1" "@w3ux/extension-assets": "npm:^1.0.0-beta.1" @@ -8920,7 +8722,7 @@ __metadata: languageName: node linkType: hard -"nock@npm:^13.5.4, nock@npm:^13.5.5": +"nock@npm:^13.5.4": version: 13.5.6 resolution: "nock@npm:13.5.6" dependencies: From ae651b74c459544ed009b8e7984376749b54cb69 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 12:13:27 +0700 Subject: [PATCH 64/84] refactor BalanceTest --- packages/app/src/modals/BalanceTest/index.tsx | 39 +- yarn.lock | 526 +++++++++--------- 2 files changed, 269 insertions(+), 296 deletions(-) diff --git a/packages/app/src/modals/BalanceTest/index.tsx b/packages/app/src/modals/BalanceTest/index.tsx index cf63a773b..7577ae2d3 100644 --- a/packages/app/src/modals/BalanceTest/index.tsx +++ b/packages/app/src/modals/BalanceTest/index.tsx @@ -2,51 +2,54 @@ // SPDX-License-Identifier: GPL-3.0-only import { unitToPlanck } from '@w3ux/utils'; -import { useApi } from 'contexts/Api'; import { useOverlay } from 'kits/Overlay/Provider'; import { useTxMeta } from 'contexts/TxMeta'; import { useBatchCall } from 'hooks/useBatchCall'; -import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; import { Close } from 'library/Modal/Close'; -import { SubmitTx } from 'library/SubmitTx'; import { useEffect } from 'react'; import { useNetwork } from 'contexts/Network'; import { useActiveAccounts } from 'contexts/ActiveAccounts'; import { ModalPadding } from 'kits/Overlay/structure/ModalPadding'; +import { ApiController } from 'controllers/Api'; +import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import { SubmitTx } from 'library/SubmitTx'; export const BalanceTest = () => { - const { api } = useApi(); const { + network, networkData: { units }, } = useNetwork(); - const { activeAccount } = useActiveAccounts(); const { notEnoughFunds } = useTxMeta(); const { newBatchCall } = useBatchCall(); - const { setModalStatus, setModalResize } = useOverlay().modal; + const { activeAccount } = useActiveAccounts(); + const { setModalResize, setModalStatus } = useOverlay().modal; // tx to submit const getTx = () => { + const { pApi } = ApiController.get(network); + const tx = null; - if (!api || !activeAccount) { + if (!pApi || !activeAccount) { return tx; } const txs = [ - api.tx.balances.transfer( - { - id: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', + pApi.tx.Balances.transfer_keep_alive({ + dest: { + type: 'Id', + value: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', }, - unitToPlanck('0.1', units).toString() - ), - api.tx.balances.transfer( - { - id: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', + value: unitToPlanck('0.1', units).toString(), + }), + pApi.tx.Balances.transfer_keep_alive({ + dest: { + type: 'Id', + value: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', }, - unitToPlanck('0.05', units).toString() - ), + value: unitToPlanck('0.1', units).toString(), + }), ]; const batch = newBatchCall(txs, activeAccount); - return batch; }; diff --git a/yarn.lock b/yarn.lock index 05b7687aa..04ac921e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -747,46 +747,46 @@ __metadata: languageName: node linkType: hard -"@fortawesome/fontawesome-common-types@npm:6.6.0": - version: 6.6.0 - resolution: "@fortawesome/fontawesome-common-types@npm:6.6.0" - checksum: 10c0/f76e5959f6ce01355f599126a3a68facba578dc8ebb7ad40fbd22417b7056364a577c1887720ec9653d4efa5b704a01150f5064fc7de237d697fd80e3d9c83aa +"@fortawesome/fontawesome-common-types@npm:6.7.1": + version: 6.7.1 + resolution: "@fortawesome/fontawesome-common-types@npm:6.7.1" + checksum: 10c0/0345b896ad8df26fb823893ce0a405dd513e9ef34878dd7b94fbbbabe370379bf680a32199bcaaf7346acbcd2a10bf0072e305829b49e5d9b0bf1c5315d07c96 languageName: node linkType: hard "@fortawesome/fontawesome-svg-core@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/fontawesome-svg-core@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/fontawesome-svg-core@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/38e2840791711524a3c57d9ea48a5a2e99da6fa3c657ba6beaad7ec3b8da31489a9e38f42b23d70584c75b579dc1ff8c67e075bc9789032278e4da54bb86ecfe + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/7a1dc40fc5ef380eb0f83d89dfcb26dc5b49f6a6545970e46e413d81dfa9764006a056cfcd49c2e8113dbd2c95d429db873710424a150e4a923b2496e11707ca languageName: node linkType: hard "@fortawesome/free-brands-svg-icons@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/free-brands-svg-icons@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/free-brands-svg-icons@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/1135a22ff274939da477496f550b6750a1b5fd0ddd0c09bddb1874f2c183a5c8edb519de2cebf6454b12a8457c3eec587bdb6f68e96140cceeb6d02c1ec35479 + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/bd140ed872582300f7ed4b2af9348791cae38217ee0685ea34c135b00ce8db07da17b518464162a29d371f2f7177d3051b02535ca6b560bb15981ef34ef8e112 languageName: node linkType: hard "@fortawesome/free-regular-svg-icons@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/free-regular-svg-icons@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/free-regular-svg-icons@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/c682a6d7c6bdce492eee5b15a6647f9c436ce04f337080b7061cc04a739b5eb95224f7cdc7d865cf08fea837d4d1b1541849a3183534956e176896a969220d45 + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/6d770622cd6519835d8557a0a562c5f2f877527bb65671a6136a558ce61024bf7baaeb8e29420f29da4893b815c3af8173c6f45064bbcc040db613998d77502e languageName: node linkType: hard "@fortawesome/free-solid-svg-icons@npm:^6.5.2": - version: 6.6.0 - resolution: "@fortawesome/free-solid-svg-icons@npm:6.6.0" + version: 6.7.1 + resolution: "@fortawesome/free-solid-svg-icons@npm:6.7.1" dependencies: - "@fortawesome/fontawesome-common-types": "npm:6.6.0" - checksum: 10c0/34828d5e682c6f9d19e3a892ff8a390128fa7dc68768b11c727c11b6a05e5efc929206bfbec83e9d3ae0590a6f6ea22fd5e447fea647e560650f7f3ef1cff543 + "@fortawesome/fontawesome-common-types": "npm:6.7.1" + checksum: 10c0/9b6e6ba383dfc456020b77a600edcccaf8131ebd472038bd6b6f2425f011c8c63c0a6049d798dc120512b9c5332043eb46bb815d9d55c56ebdf8ecc94afb0184 languageName: node linkType: hard @@ -1077,9 +1077,9 @@ __metadata: linkType: hard "@kurkle/color@npm:^0.3.0": - version: 0.3.2 - resolution: "@kurkle/color@npm:0.3.2" - checksum: 10c0/a9e8e3e35dcd59dec4dd4f0105919c05e24823a96347bcf152965c29e48d6290b66d5fb96c071875db752e10930724c48ce6d338fefbd65e0ce5082d5c78970e + version: 0.3.4 + resolution: "@kurkle/color@npm:0.3.4" + checksum: 10c0/0e9fd55c614b005c5f0c4c755bca19ec0293bc7513b4ea3ec1725234f9c2fa81afbc78156baf555c8b9cb0d305619253c3f5bca016067daeebb3d00ebb4ea683 languageName: node linkType: hard @@ -1760,84 +1760,72 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/cli@npm:0.9.17": - version: 0.9.17 - resolution: "@polkadot-api/cli@npm:0.9.17" +"@polkadot-api/cli@npm:0.9.20": + version: 0.9.20 + resolution: "@polkadot-api/cli@npm:0.9.20" dependencies: "@commander-js/extra-typings": "npm:^12.1.0" - "@polkadot-api/codegen": "npm:0.12.7" - "@polkadot-api/ink-contracts": "npm:0.2.0" + "@polkadot-api/codegen": "npm:0.12.8" + "@polkadot-api/ink-contracts": "npm:0.2.1" "@polkadot-api/json-rpc-provider": "npm:0.0.4" - "@polkadot-api/known-chains": "npm:0.5.6" + "@polkadot-api/known-chains": "npm:0.5.7" "@polkadot-api/metadata-compatibility": "npm:0.1.11" "@polkadot-api/observable-client": "npm:0.6.2" "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" - "@polkadot-api/sm-provider": "npm:0.1.5" - "@polkadot-api/smoldot": "npm:0.3.5" + "@polkadot-api/sm-provider": "npm:0.1.7" + "@polkadot-api/smoldot": "npm:0.3.7" "@polkadot-api/substrate-bindings": "npm:0.9.3" "@polkadot-api/substrate-client": "npm:0.3.0" "@polkadot-api/utils": "npm:0.1.2" - "@polkadot-api/wasm-executor": "npm:^0.1.1" - "@polkadot-api/ws-provider": "npm:0.3.4" - "@types/node": "npm:^22.2.0" + "@polkadot-api/wasm-executor": "npm:^0.1.2" + "@polkadot-api/ws-provider": "npm:0.3.6" + "@types/node": "npm:^22.9.0" commander: "npm:^12.1.0" - execa: "npm:^9.3.0" + execa: "npm:^9.5.1" fs.promises.exists: "npm:^1.1.4" - ora: "npm:^8.0.1" + ora: "npm:^8.1.1" read-pkg: "npm:^9.0.1" rxjs: "npm:^7.8.1" tsc-prog: "npm:^2.3.0" - tsup: "npm:^8.2.4" - typescript: "npm:^5.5.4" + tsup: "npm:^8.3.5" + typescript: "npm:^5.6.3" write-package: "npm:^7.1.0" bin: papi: dist/main.js polkadot-api: dist/main.js - checksum: 10c0/0bdbe91a11e07b0dcc730594c8adfe69fe7e522e7824f07feac51203dec1696e7ba050df209880567467b2813d20559ed85e23a62f71a179c348369f59574265 + checksum: 10c0/579b3dbc6c291a80aa4963745db41f2879727c4c811282bea8ba07e45ee130a524f2b9bcd142b9af3aed68740dbdf798a0f9e15a2167d94cf35459f96b7855da languageName: node linkType: hard -"@polkadot-api/codegen@npm:0.12.7": - version: 0.12.7 - resolution: "@polkadot-api/codegen@npm:0.12.7" +"@polkadot-api/codegen@npm:0.12.8": + version: 0.12.8 + resolution: "@polkadot-api/codegen@npm:0.12.8" dependencies: - "@polkadot-api/ink-contracts": "npm:0.1.2" + "@polkadot-api/ink-contracts": "npm:0.2.1" "@polkadot-api/metadata-builders": "npm:0.9.1" "@polkadot-api/metadata-compatibility": "npm:0.1.11" "@polkadot-api/substrate-bindings": "npm:0.9.3" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/3330e74627123d24e0b9c572c172f832060d55b84e87986c8712ee17d9b84e299d90c3f06ff2b8aa1561a21219492bddce723e4c84e16c570f262028aea32333 - languageName: node - linkType: hard - -"@polkadot-api/ink-contracts@npm:0.1.2": - version: 0.1.2 - resolution: "@polkadot-api/ink-contracts@npm:0.1.2" - dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" - "@polkadot-api/utils": "npm:0.1.2" - scale-ts: "npm:^1.6.1" - checksum: 10c0/334203d4c981b691c8f0215746787a7664186c2947ba89818e58034643ab4f552ead612f8cf0b1f6cf29ad13696433717f01a75fde00fd6cd7a90cf0492e61f1 + checksum: 10c0/18f1be81133ff07eb2ea6931bb1091ee5fde19f6943042d98b2126c168d870740202dc236c4783767b00ff35e40040e09ebc28d12ed4075228bfdadbf5b9d3f0 languageName: node linkType: hard -"@polkadot-api/ink-contracts@npm:0.2.0": - version: 0.2.0 - resolution: "@polkadot-api/ink-contracts@npm:0.2.0" +"@polkadot-api/ink-contracts@npm:0.2.1": + version: 0.2.1 + resolution: "@polkadot-api/ink-contracts@npm:0.2.1" dependencies: "@polkadot-api/metadata-builders": "npm:0.9.1" "@polkadot-api/substrate-bindings": "npm:0.9.3" "@polkadot-api/utils": "npm:0.1.2" scale-ts: "npm:^1.6.1" - checksum: 10c0/c5f2457452047c0511bc3092cf61fbe1aafdc665fe5a8b60227fce57f587effea3e1f8d145ae3e6db1631a84286f40847cbf2c74f4f580e643151d2f09b92676 + checksum: 10c0/a6df3a780d41d577832576790beb355132d86f8089d1886fc5e78250e3de3556ade9571e71f893b93eebb3476f8c82804eb477d626ed26c993dc2ea3b4d9ef20 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 +"@polkadot-api/json-rpc-provider-proxy@npm:0.2.4": + version: 0.2.4 + resolution: "@polkadot-api/json-rpc-provider-proxy@npm:0.2.4" + checksum: 10c0/bf99d5a27e02dc5b3641d4c17ec28d7ac72b7ad077e99a311f3c2709000957429321ae93926c941024539c521a118d45251fc8ab3f8fc11334e1385a2f8908ff languageName: node linkType: hard @@ -1862,10 +1850,10 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/known-chains@npm:0.5.6": - version: 0.5.6 - resolution: "@polkadot-api/known-chains@npm:0.5.6" - checksum: 10c0/1767760ecdf834540290bdde8ab823907121b0ee3fee071bb393f00625b419299168c71cbcfaa6e6af67789cc5f4e4083a0020c955540b5f379b120895b44556 +"@polkadot-api/known-chains@npm:0.5.7": + version: 0.5.7 + resolution: "@polkadot-api/known-chains@npm:0.5.7" + checksum: 10c0/5ba499e58f0066346a48bda459b23570c849fb267a10c8a23494661c563ea7763b560d4d3fa95283b9cb6767d1a1af651d23c94ac7550050d2f5f874569e29d1 languageName: node linkType: hard @@ -2001,25 +1989,25 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/sm-provider@npm:0.1.5": - version: 0.1.5 - resolution: "@polkadot-api/sm-provider@npm:0.1.5" +"@polkadot-api/sm-provider@npm:0.1.7": + version: 0.1.7 + resolution: "@polkadot-api/sm-provider@npm:0.1.7" dependencies: "@polkadot-api/json-rpc-provider": "npm:0.0.4" - "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.3" + "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.4" peerDependencies: "@polkadot-api/smoldot": ">=0.3" - checksum: 10c0/d87dfc11d5d491c0bdb35e2a788641c8e1ec738d47885c1929c29d22ea03eaaf14a7ea355507b8817a42f9d37f0ae265ee138e6fd46618ca47cc6622046a302b + checksum: 10c0/91a1b8cc054f5b8f5ec25a40d9bc876ad5e9138a9801b22a2e4f6bf396ed584956cff0de7349d9b435fe5c04bad9137e59edcb6960943b9126e7262c9a0d8740 languageName: node linkType: hard -"@polkadot-api/smoldot@npm:0.3.5": - version: 0.3.5 - resolution: "@polkadot-api/smoldot@npm:0.3.5" +"@polkadot-api/smoldot@npm:0.3.7": + version: 0.3.7 + resolution: "@polkadot-api/smoldot@npm:0.3.7" dependencies: - "@types/node": "npm:^22.2.0" - smoldot: "npm:2.0.31" - checksum: 10c0/a9658a06bf4a3bcb5127dcf3a58adf732c43be4c683cdd7054dadfb55122f32ce990aab866deb0a572340faadfdf9813ef2f938ae220be58628be67eeb14342d + "@types/node": "npm:^22.9.0" + smoldot: "npm:2.0.33" + checksum: 10c0/edd2e4b31a3f43231e86dc9452c8cf9c126858c48539d35d5f01146a6958db8f530dc01a9aaf01ffb8b7f0b3750cdc16223df80d548e212955de889defced114 languageName: node linkType: hard @@ -2081,21 +2069,21 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/wasm-executor@npm:^0.1.1": +"@polkadot-api/wasm-executor@npm:^0.1.2": version: 0.1.2 resolution: "@polkadot-api/wasm-executor@npm:0.1.2" checksum: 10c0/0577111cf7ee12e1606ba863385f50670f2d073e9032efd8365e578f03f4a8b01e699641e19b2aec15e36832aa3b6b0724951fa84d23e87492d0532f55e291bf languageName: node linkType: hard -"@polkadot-api/ws-provider@npm:0.3.4": - version: 0.3.4 - resolution: "@polkadot-api/ws-provider@npm:0.3.4" +"@polkadot-api/ws-provider@npm:0.3.6": + version: 0.3.6 + resolution: "@polkadot-api/ws-provider@npm:0.3.6" dependencies: "@polkadot-api/json-rpc-provider": "npm:0.0.4" - "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.3" + "@polkadot-api/json-rpc-provider-proxy": "npm:0.2.4" ws: "npm:^8.18.0" - checksum: 10c0/39243a8f1545dd8191c62f43ee05e2450b5a7758adfe974f2d4a965546ea5001f19ff7ca14c87d1655dba1526ad08f8118a2cc6597f53d3a3172860b5da4d10d + checksum: 10c0/902c599ef02ddc3564cd4517aa3235262de1523a6879f81f94d73c90c260990fd595db7eacb2184670d9aa811eddc5eb346e78f74a60677846d991457e014ce9 languageName: node linkType: hard @@ -2792,128 +2780,128 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.26.0" +"@rollup/rollup-android-arm-eabi@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.27.3" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-android-arm64@npm:4.26.0" +"@rollup/rollup-android-arm64@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-android-arm64@npm:4.27.3" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-darwin-arm64@npm:4.26.0" +"@rollup/rollup-darwin-arm64@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-darwin-arm64@npm:4.27.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-darwin-x64@npm:4.26.0" +"@rollup/rollup-darwin-x64@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-darwin-x64@npm:4.27.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.26.0" +"@rollup/rollup-freebsd-arm64@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.27.3" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-freebsd-x64@npm:4.26.0" +"@rollup/rollup-freebsd-x64@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-freebsd-x64@npm:4.27.3" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.26.0" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.27.3" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.26.0" +"@rollup/rollup-linux-arm-musleabihf@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.27.3" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.26.0" +"@rollup/rollup-linux-arm64-gnu@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.27.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.26.0" +"@rollup/rollup-linux-arm64-musl@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.27.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.26.0" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.3" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.26.0" +"@rollup/rollup-linux-riscv64-gnu@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.27.3" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.26.0" +"@rollup/rollup-linux-s390x-gnu@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.27.3" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.26.0" +"@rollup/rollup-linux-x64-gnu@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.27.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.26.0" +"@rollup/rollup-linux-x64-musl@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.27.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.26.0" +"@rollup/rollup-win32-arm64-msvc@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.27.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.26.0" +"@rollup/rollup-win32-ia32-msvc@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.27.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.26.0": - version: 4.26.0 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.26.0" +"@rollup/rollup-win32-x64-msvc@npm:4.27.3": + version: 4.27.3 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.27.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2946,9 +2934,9 @@ __metadata: linkType: hard "@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": - version: 3.22.2 - resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.2" - checksum: 10c0/4c61c6bc1e720ceb98e7812ef060e40120e130c385f1ac8012a99155179b0651e12f608e053c9e4d1d7917881920e9e3b15c3c90805f9bbb7f28d80b13d04381 + version: 3.22.3 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.3" + checksum: 10c0/17cb09945e845556332f65785f1aa6a2d4b7c319a87f6fde48263a88e6b2aeaa6f9bb82b4f12a471d1c6a5f2d03465d2ae34a7730d790ab51349d2039aacfa37 languageName: node linkType: hard @@ -3528,11 +3516,11 @@ __metadata: linkType: hard "@swc/types@npm:^0.1.15": - version: 0.1.15 - resolution: "@swc/types@npm:0.1.15" + version: 0.1.16 + resolution: "@swc/types@npm:0.1.16" dependencies: "@swc/counter": "npm:^0.1.3" - checksum: 10c0/82bcfa64e53c6c93ae162fe9e491e5f300227fad6f110e32d9718e5a0e29586bc79c516234f6eccbe5ccd7ed72b514a21f03196a54408cf1b7b47c072fad44f0 + checksum: 10c0/ca079125ef4c619a4ae908a9d6b99e526e0e21d54443585da9904e9e67a3e7320189843639c6e4435d807dfae5837ab3083a18e86f698e1decf00ecc277ab0ec languageName: node linkType: hard @@ -3624,12 +3612,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:^22.2.0": - version: 22.9.0 - resolution: "@types/node@npm:22.9.0" +"@types/node@npm:*, @types/node@npm:^22.9.0": + version: 22.9.1 + resolution: "@types/node@npm:22.9.1" dependencies: undici-types: "npm:~6.19.8" - checksum: 10c0/3f46cbe0a49bab4ba30494025e4c8a6e699b98ac922857aa1f0209ce11a1313ee46e6808b8f13fe5b8b960a9d7796b77c8d542ad4e9810e85ef897d5593b5d51 + checksum: 10c0/ea489ae603aa8874e4e88980aab6f2dad09c755da779c88dd142983bfe9609803c89415ca7781f723072934066f63daf2b3339ef084a8ad1a8079cf3958be243 languageName: node linkType: hard @@ -3972,7 +3960,7 @@ __metadata: languageName: node linkType: hard -"@w3ux/utils@npm:^1.1.1-beta.11": +"@w3ux/utils@npm:^1.1.1-beta.11, @w3ux/utils@npm:^1.1.1-beta.9": version: 1.1.1-beta.11 resolution: "@w3ux/utils@npm:1.1.1-beta.11" dependencies: @@ -3981,15 +3969,6 @@ __metadata: languageName: node linkType: hard -"@w3ux/utils@npm:^1.1.1-beta.9": - version: 1.1.1-beta.9 - resolution: "@w3ux/utils@npm:1.1.1-beta.9" - dependencies: - "@polkadot-api/substrate-bindings": "npm:^0.9.3" - checksum: 10c0/b18cf8274d43cf3419e8b47fc822164653da7a48a83a6eb84820b9fe14386e5f167ad1f634bfddac938f472839bc605e9bb04e8a88cfd12223b5b40b24730934 - languageName: node - linkType: hard - "@w3ux/validator-assets@npm:^0.2.0": version: 0.2.0 resolution: "@w3ux/validator-assets@npm:0.2.0" @@ -3999,9 +3978,9 @@ __metadata: languageName: node linkType: hard -"@wagmi/connectors@npm:5.3.10": - version: 5.3.10 - resolution: "@wagmi/connectors@npm:5.3.10" +"@wagmi/connectors@npm:5.5.0": + version: 5.5.0 + resolution: "@wagmi/connectors@npm:5.5.0" dependencies: "@coinbase/wallet-sdk": "npm:4.2.3" "@metamask/sdk": "npm:0.30.1" @@ -4010,19 +3989,19 @@ __metadata: "@walletconnect/ethereum-provider": "npm:2.17.0" cbw-sdk: "npm:@coinbase/wallet-sdk@3.9.3" peerDependencies: - "@wagmi/core": 2.14.6 + "@wagmi/core": 2.15.0 typescript: ">=5.0.4" viem: 2.x peerDependenciesMeta: typescript: optional: true - checksum: 10c0/683affe0fe0a319fa7e84d9eca6069a12fed288c9caea589ef6547af530111a18538546babc15719b023cbbf1f324ccc59c668163aed7cc21ab84bb906f84cca + checksum: 10c0/bbf2e74b3f35725a5ea410763b4d7cbe41dab55bffa55204cf42c20d25a3368480223f018497c8493137c0fe0df28810e94008da7c379ec859e544cb98c95553 languageName: node linkType: hard -"@wagmi/core@npm:2.14.6": - version: 2.14.6 - resolution: "@wagmi/core@npm:2.14.6" +"@wagmi/core@npm:2.15.0": + version: 2.15.0 + resolution: "@wagmi/core@npm:2.15.0" dependencies: eventemitter3: "npm:5.0.1" mipd: "npm:0.0.7" @@ -4036,7 +4015,7 @@ __metadata: optional: true typescript: optional: true - checksum: 10c0/bc79ba678f00da5e769526875698e9dc1464fc650f3db27ecf9865b78f0690b7006bb36b0ea0acf6deb9ea5d5a84d343fc8ec6efaa9e9a73868ddca9a8eb046e + checksum: 10c0/9f8889e6b58677287799f4faccf6910b57d5ea8bdb12a6a4faf930a6a8c45dc3cb641e4eba697011ad195601a33fe30aea1a700d6dbc853de5138e3268f1cfb4 languageName: node linkType: hard @@ -4384,15 +4363,6 @@ __metadata: languageName: node linkType: hard -"@wry/trie@npm:^0.4.3": - version: 0.4.3 - resolution: "@wry/trie@npm:0.4.3" - dependencies: - tslib: "npm:^2.3.0" - checksum: 10c0/1a14edba595b1967d0cf38208c2660b2952a8e8a649bb669b67907df48f602c7f2acbe16c1e1b115afa7d7effb9f1a4dbde38eef16ee92e7521a511262a53281 - languageName: node - linkType: hard - "@wry/trie@npm:^0.5.0": version: 0.5.0 resolution: "@wry/trie@npm:0.5.0" @@ -5038,9 +5008,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001669": - version: 1.0.30001680 - resolution: "caniuse-lite@npm:1.0.30001680" - checksum: 10c0/11a4e7f6f5d5f965cfd4b7dc4aef34e12a26e99647f02b5ac9fd7f7670845473b95ada416a785473237e4b1b67281f7b043c8736c85b77097f6b697e8950b15f + version: 1.0.30001682 + resolution: "caniuse-lite@npm:1.0.30001682" + checksum: 10c0/de9d8bc08e293f8bf6e98e9035055920a75042a655c6abacbf0a4af5ec77882d26df4489721a7f723559c817b13f3fee86b35e827ee2383a5cdf7fb41c60dcd0 languageName: node linkType: hard @@ -5400,13 +5370,13 @@ __metadata: linkType: hard "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": - version: 7.0.5 - resolution: "cross-spawn@npm:7.0.5" + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" dependencies: path-key: "npm:^3.1.0" shebang-command: "npm:^2.0.0" which: "npm:^2.0.1" - checksum: 10c0/aa82ce7ac0814a27e6f2b738c5a7cf1fa21a3558a1e42df449fc96541ba3ba731e4d3ecffa4435348808a86212f287c6f20a1ee551ef1ff95d01cfec5f434944 + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 languageName: node linkType: hard @@ -5716,21 +5686,21 @@ __metadata: linkType: hard "eciesjs@npm:^0.4.8": - version: 0.4.11 - resolution: "eciesjs@npm:0.4.11" + version: 0.4.12 + resolution: "eciesjs@npm:0.4.12" dependencies: "@ecies/ciphers": "npm:^0.2.1" "@noble/ciphers": "npm:^1.0.0" "@noble/curves": "npm:^1.6.0" "@noble/hashes": "npm:^1.5.0" - checksum: 10c0/3b707a46313d6a7629b37e6f86bb72b038f3acef994b270e5683644278bdc2278a4b125beb6a010ba2a3944c2ada469a7d3e87ff7cbd50b808f191c03530ea54 + checksum: 10c0/c2fc1afc9cfe79e89c13f82d7b9441d503b7cacf99b6cd221d75acf78678fde401608b7c49fd54965b5a83c6d36d04b510b78c3dc1499d8bc8cfcaea84666aab languageName: node linkType: hard "electron-to-chromium@npm:^1.5.41": - version: 1.5.57 - resolution: "electron-to-chromium@npm:1.5.57" - checksum: 10c0/42b969681985016be6069ae68cf29e84ba3f2191fcb7f9d3355e83e81da8dbd100e4b5c9d69b88637003e06dc1860125a50332ec0caee49fd9c2c4ab62feb288 + version: 1.5.63 + resolution: "electron-to-chromium@npm:1.5.63" + checksum: 10c0/fe1b175805309b04e5a2242c3168f22543e5369aed01fceedfe0f0eafe3931e8609d8a140e527394b314cfe64d581913aba6f1d3c72c23069c7d8241e5dfa4ef languageName: node linkType: hard @@ -5865,8 +5835,8 @@ __metadata: linkType: hard "es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.1, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3": - version: 1.23.4 - resolution: "es-abstract@npm:1.23.4" + version: 1.23.5 + resolution: "es-abstract@npm:1.23.5" dependencies: array-buffer-byte-length: "npm:^1.0.1" arraybuffer.prototype.slice: "npm:^1.0.3" @@ -5914,7 +5884,7 @@ __metadata: typed-array-length: "npm:^1.0.6" unbox-primitive: "npm:^1.0.2" which-typed-array: "npm:^1.1.15" - checksum: 10c0/70c56ec479d57e63387f561bb50c80587f4a52010868787e3d4b4f95301edf5ba98d70ffd0ba56eb4722c48c578870ff2a8825236a948cfa483f76015d134acb + checksum: 10c0/1f6f91da9cf7ee2c81652d57d3046621d598654d1d1b05c1578bafe5c4c2d3d69513901679bdca2de589f620666ec21de337e4935cec108a4ed0871d5ef04a5d languageName: node linkType: hard @@ -6592,7 +6562,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^9.3.0": +"execa@npm:^9.5.1": version: 9.5.1 resolution: "execa@npm:9.5.1" dependencies: @@ -6788,9 +6758,9 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.3.1 - resolution: "flatted@npm:3.3.1" - checksum: 10c0/324166b125ee07d4ca9bcf3a5f98d915d5db4f39d711fba640a3178b959919aae1f7cfd8aabcfef5826ed8aa8a2aa14cc85b2d7d18ff638ddf4ae3df39573eaf + version: 3.3.2 + resolution: "flatted@npm:3.3.2" + checksum: 10c0/24cc735e74d593b6c767fe04f2ef369abe15b62f6906158079b9874bdb3ee5ae7110bb75042e70cd3f99d409d766f357caf78d5ecee9780206f5fdc5edbad334 languageName: node linkType: hard @@ -6844,8 +6814,8 @@ __metadata: linkType: hard "framer-motion@npm:^11.11.1": - version: 11.11.15 - resolution: "framer-motion@npm:11.11.15" + version: 11.11.17 + resolution: "framer-motion@npm:11.11.17" dependencies: tslib: "npm:^2.4.0" peerDependencies: @@ -6859,7 +6829,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 10c0/515d1f758ebf8c7257f80586fe459acbb77f323e7a2f3e8b20bf53023960f5e54b1cf364b39fa2ded2ec0558c66e78c681b3903cfac11fefdb51efcfaff21443 + checksum: 10c0/4ec88a568e636c5409a238668770a7d0237270b4b58fdf27d0cab1f9094a4fdd22d25d827a1bba51e52f01e9d2db65544b32a5feb8eff9a63c8798f2e64677d0 languageName: node linkType: hard @@ -7390,11 +7360,11 @@ __metadata: linkType: hard "i18next@npm:^23.11.5": - version: 23.16.5 - resolution: "i18next@npm:23.16.5" + version: 23.16.8 + resolution: "i18next@npm:23.16.8" dependencies: "@babel/runtime": "npm:^7.23.2" - checksum: 10c0/4f926c09dc1087041084dbbfb8307bf2fdba1981069dece1c563577ca8a38a23ebfadf4d1c50c4091b640a8c8bf81d9cd008bd3e587bd452a6b9c728d5065346 + checksum: 10c0/57d249191e8a39bbbbe190cfa2e2bb651d0198e14444fe80453d3df8d02927de3c147c77724e9ae6c72fa241898cd761e3fdcd55d053db373471f1ac084bf345 languageName: node linkType: hard @@ -8257,12 +8227,12 @@ __metadata: linkType: hard "local-pkg@npm:^0.5.0": - version: 0.5.0 - resolution: "local-pkg@npm:0.5.0" + version: 0.5.1 + resolution: "local-pkg@npm:0.5.1" dependencies: - mlly: "npm:^1.4.2" - pkg-types: "npm:^1.0.3" - checksum: 10c0/f61cbd00d7689f275558b1a45c7ff2a3ddf8472654123ed880215677b9adfa729f1081e50c27ffb415cdb9fa706fb755fec5e23cdd965be375c8059e87ff1cc9 + mlly: "npm:^1.7.3" + pkg-types: "npm:^1.2.1" + checksum: 10c0/ade8346f1dc04875921461adee3c40774b00d4b74095261222ebd4d5fd0a444676e36e325f76760f21af6a60bc82480e154909b54d2d9f7173671e36dacf1808 languageName: node linkType: hard @@ -8382,11 +8352,11 @@ __metadata: linkType: hard "magic-string@npm:^0.30.5": - version: 0.30.12 - resolution: "magic-string@npm:0.30.12" + version: 0.30.13 + resolution: "magic-string@npm:0.30.13" dependencies: "@jridgewell/sourcemap-codec": "npm:^1.5.0" - checksum: 10c0/469f457d18af37dfcca8617086ea8a65bcd8b60ba8a1182cb024ce43e470ace3c9d1cb6bee58d3b311768fb16bc27bd50bdeebcaa63dadd0fd46cac4d2e11d5f + checksum: 10c0/a275faeca1564c545019b4742c38a42ca80226c8c9e0805c32d1a1cc58b0e6ff7bbd914ed885fd10043858a7da0f732cb8f49c8975c3ecebde9cad4b57db5115 languageName: node linkType: hard @@ -8624,7 +8594,7 @@ __metadata: languageName: node linkType: hard -"mlly@npm:^1.4.2, mlly@npm:^1.7.1, mlly@npm:^1.7.2": +"mlly@npm:^1.7.1, mlly@npm:^1.7.2, mlly@npm:^1.7.3": version: 1.7.3 resolution: "mlly@npm:1.7.3" dependencies: @@ -8798,13 +8768,13 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.2.0, node-gyp-build@npm:^4.3.0": - version: 4.8.3 - resolution: "node-gyp-build@npm:4.8.3" + version: 4.8.4 + resolution: "node-gyp-build@npm:4.8.4" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 10c0/a7f43c4128d817db80bb0884f631121449ac586b4a3e708eab0db6fcb7fa0d2e66f6d7d4ee1f49469409de4a9b2e413926befe2dce70b850c6c583a3bbe228d2 + checksum: 10c0/444e189907ece2081fe60e75368784f7782cfddb554b60123743dfb89509df89f1f29c03bbfa16b3a3e0be3f48799a4783f487da6203245fa5bed239ba7407e1 languageName: node linkType: hard @@ -9045,14 +9015,14 @@ __metadata: linkType: hard "optimism@npm:^0.18.0": - version: 0.18.0 - resolution: "optimism@npm:0.18.0" + version: 0.18.1 + resolution: "optimism@npm:0.18.1" dependencies: "@wry/caches": "npm:^1.0.0" "@wry/context": "npm:^0.7.0" - "@wry/trie": "npm:^0.4.3" + "@wry/trie": "npm:^0.5.0" tslib: "npm:^2.3.0" - checksum: 10c0/8e97c6d660cb80cf5f444209b9dd29ee6951fa7b344d4c4fc6d4aaf0ad0710dddaf834d0f5d7211b3658b15ef6c6a22cbcb98c7a8121e3fee9666fe0fd62d876 + checksum: 10c0/1c1cd065d306de2220c6a2bdd8701cb7f9aadace36a9f16d6e02db2bee23b0291f15a1219b92cde5c66d816bd33dca876dfdcdbad04b4cf9b2a7fc5a1a221e77 languageName: node linkType: hard @@ -9070,7 +9040,7 @@ __metadata: languageName: node linkType: hard -"ora@npm:^8.0.1": +"ora@npm:^8.1.1": version: 8.1.1 resolution: "ora@npm:8.1.1" dependencies: @@ -9367,7 +9337,7 @@ __metadata: languageName: node linkType: hard -"pkg-types@npm:^1.0.3, pkg-types@npm:^1.2.1": +"pkg-types@npm:^1.2.1": version: 1.2.1 resolution: "pkg-types@npm:1.2.1" dependencies: @@ -9395,13 +9365,13 @@ __metadata: linkType: hard "polkadot-api@npm:^1.7.3": - version: 1.7.3 - resolution: "polkadot-api@npm:1.7.3" + version: 1.7.6 + resolution: "polkadot-api@npm:1.7.6" dependencies: - "@polkadot-api/cli": "npm:0.9.17" - "@polkadot-api/ink-contracts": "npm:0.2.0" + "@polkadot-api/cli": "npm:0.9.20" + "@polkadot-api/ink-contracts": "npm:0.2.1" "@polkadot-api/json-rpc-provider": "npm:0.0.4" - "@polkadot-api/known-chains": "npm:0.5.6" + "@polkadot-api/known-chains": "npm:0.5.7" "@polkadot-api/logs-provider": "npm:0.0.6" "@polkadot-api/metadata-builders": "npm:0.9.1" "@polkadot-api/metadata-compatibility": "npm:0.1.11" @@ -9410,18 +9380,18 @@ __metadata: "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" "@polkadot-api/polkadot-signer": "npm:0.1.6" "@polkadot-api/signer": "npm:0.1.10" - "@polkadot-api/sm-provider": "npm:0.1.5" - "@polkadot-api/smoldot": "npm:0.3.5" + "@polkadot-api/sm-provider": "npm:0.1.7" + "@polkadot-api/smoldot": "npm:0.3.7" "@polkadot-api/substrate-bindings": "npm:0.9.3" "@polkadot-api/substrate-client": "npm:0.3.0" "@polkadot-api/utils": "npm:0.1.2" - "@polkadot-api/ws-provider": "npm:0.3.4" + "@polkadot-api/ws-provider": "npm:0.3.6" peerDependencies: rxjs: ">=7.8.0" bin: papi: bin/cli.mjs polkadot-api: bin/cli.mjs - checksum: 10c0/ac430cd00d51308f65f3f8a1d7b405d3d1d86726c47481dfa2cfb2c08c36dda3cb51f0a880a00c7fa677e84bae3385de91e3da6627492fad89848b5a3e94b6fa + checksum: 10c0/caa2df461f2a252aeb8b1b9076bc096a4c6952015a88ac004defc2aac2ea385349767db5394e6a98fef8fdf28eaadbfbcc1914fbacc14d108923f13ed6cf1a12 languageName: node linkType: hard @@ -9583,11 +9553,11 @@ __metadata: linkType: hard "pretty-ms@npm:^9.0.0": - version: 9.1.0 - resolution: "pretty-ms@npm:9.1.0" + version: 9.2.0 + resolution: "pretty-ms@npm:9.2.0" dependencies: parse-ms: "npm:^4.0.0" - checksum: 10c0/fd111aad8800a04dfd654e6016da69bdaa6fc6a4c280f8e727cffd8b5960558e94942f1a94d4aa6e4d179561a0fbb0366a9ebe0ccefbbb0f8ff853b129cdefb9 + checksum: 10c0/ab6d066f90e9f77020426986e1b018369f41575674544c539aabec2e63a20fec01166d8cf6571d0e165ad11cfe5a8134a2a48a36d42ab291c59c6deca5264cbb languageName: node linkType: hard @@ -10218,27 +10188,27 @@ __metadata: linkType: hard "rollup@npm:^4.20.0, rollup@npm:^4.24.0": - version: 4.26.0 - resolution: "rollup@npm:4.26.0" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.26.0" - "@rollup/rollup-android-arm64": "npm:4.26.0" - "@rollup/rollup-darwin-arm64": "npm:4.26.0" - "@rollup/rollup-darwin-x64": "npm:4.26.0" - "@rollup/rollup-freebsd-arm64": "npm:4.26.0" - "@rollup/rollup-freebsd-x64": "npm:4.26.0" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.26.0" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.26.0" - "@rollup/rollup-linux-arm64-gnu": "npm:4.26.0" - "@rollup/rollup-linux-arm64-musl": "npm:4.26.0" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.26.0" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.26.0" - "@rollup/rollup-linux-s390x-gnu": "npm:4.26.0" - "@rollup/rollup-linux-x64-gnu": "npm:4.26.0" - "@rollup/rollup-linux-x64-musl": "npm:4.26.0" - "@rollup/rollup-win32-arm64-msvc": "npm:4.26.0" - "@rollup/rollup-win32-ia32-msvc": "npm:4.26.0" - "@rollup/rollup-win32-x64-msvc": "npm:4.26.0" + version: 4.27.3 + resolution: "rollup@npm:4.27.3" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.27.3" + "@rollup/rollup-android-arm64": "npm:4.27.3" + "@rollup/rollup-darwin-arm64": "npm:4.27.3" + "@rollup/rollup-darwin-x64": "npm:4.27.3" + "@rollup/rollup-freebsd-arm64": "npm:4.27.3" + "@rollup/rollup-freebsd-x64": "npm:4.27.3" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.27.3" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.27.3" + "@rollup/rollup-linux-arm64-gnu": "npm:4.27.3" + "@rollup/rollup-linux-arm64-musl": "npm:4.27.3" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.27.3" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.27.3" + "@rollup/rollup-linux-s390x-gnu": "npm:4.27.3" + "@rollup/rollup-linux-x64-gnu": "npm:4.27.3" + "@rollup/rollup-linux-x64-musl": "npm:4.27.3" + "@rollup/rollup-win32-arm64-msvc": "npm:4.27.3" + "@rollup/rollup-win32-ia32-msvc": "npm:4.27.3" + "@rollup/rollup-win32-x64-msvc": "npm:4.27.3" "@types/estree": "npm:1.0.6" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -10282,7 +10252,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/a4375787b95bc3b55d38bbb8dec5f6a63862b08369b9562a2d38efadd400ca42a79406b8f09670a0deb0cc9cd72cca1c0be317302190d1f7feff597003d951bc + checksum: 10c0/789885d3f852ed7ca45bed14194a2ac7a2cf16b6b62b54f691c79e27d5557d31a2d612d3680c26c527a1957e0bd6811806ddd765e0dae589404cf24544ff2838 languageName: node linkType: hard @@ -10615,12 +10585,12 @@ __metadata: languageName: node linkType: hard -"smoldot@npm:2.0.31": - version: 2.0.31 - resolution: "smoldot@npm:2.0.31" +"smoldot@npm:2.0.33": + version: 2.0.33 + resolution: "smoldot@npm:2.0.33" dependencies: ws: "npm:^8.8.1" - checksum: 10c0/610bf1ae1a80e7f682af144e2d0088e4d49949089c7b8bc29961ec112e120af78b7bf181718ad5f923cfca69d6b5ce23894ab10872dac6595ca1554277606977 + checksum: 10c0/dada517972924017077b4e8c6f76169ddcd371c270e714db42e034c0c974898a15f640759576b712f9d0aa3e2f1bb01c7e5d2e711e329f0f6837902ac51bb1fa languageName: node linkType: hard @@ -11360,7 +11330,7 @@ __metadata: languageName: node linkType: hard -"tsup@npm:^8.2.4": +"tsup@npm:^8.3.5": version: 8.3.5 resolution: "tsup@npm:8.3.5" dependencies: @@ -11432,9 +11402,9 @@ __metadata: linkType: hard "type-fest@npm:^4.23.0, type-fest@npm:^4.6.0, type-fest@npm:^4.7.1": - version: 4.26.1 - resolution: "type-fest@npm:4.26.1" - checksum: 10c0/d2719ff8d380befe8a3c61068f37f28d6fa2849fd140c5d2f0f143099e371da6856aad7c97e56b83329d45bfe504afe9fd936a7cff600cc0d46aa9ffb008d6c6 + version: 4.27.0 + resolution: "type-fest@npm:4.27.0" + checksum: 10c0/30c8ebe1219725021f5b5006081bd828c4e915de0de65e9a87065f566a96ece706846b2c874ac04c0dcfdfe6b4ae47ad15d3285c9aab162cb79c026dfb2e7556 languageName: node linkType: hard @@ -11490,7 +11460,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.4.3, typescript@npm:^5.5.4": +"typescript@npm:^5.4.3, typescript@npm:^5.6.3": version: 5.6.3 resolution: "typescript@npm:5.6.3" bin: @@ -11500,7 +11470,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.4.3#optional!builtin, typescript@patch:typescript@npm%3A^5.5.4#optional!builtin": +"typescript@patch:typescript@npm%3A^5.4.3#optional!builtin, typescript@patch:typescript@npm%3A^5.6.3#optional!builtin": version: 5.6.3 resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=5adc0c" bin: @@ -11839,8 +11809,8 @@ __metadata: linkType: hard "viem@npm:^2.1.1, viem@npm:^2.21.35": - version: 2.21.45 - resolution: "viem@npm:2.21.45" + version: 2.21.48 + resolution: "viem@npm:2.21.48" dependencies: "@noble/curves": "npm:1.6.0" "@noble/hashes": "npm:1.5.0" @@ -11856,7 +11826,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/3ed93c58dc125c97f35970b17d3dca6dfe6a8811d45fed84dc5bdd4a57860c880ae5a41a45cb8caa500925450b52d3a225d8def8422e7dd7ecead3bcc3921ec8 + checksum: 10c0/e9b2799535263a859bddda25d962b13d2c76aec191e1849dd0f268c32a43eb65932a05cc5be270c92e19d79aafda73884690c0b0fbdb9311266a01ea3f659082 languageName: node linkType: hard @@ -11969,8 +11939,8 @@ __metadata: linkType: hard "vite-tsconfig-paths@npm:^5.0.1": - version: 5.1.2 - resolution: "vite-tsconfig-paths@npm:5.1.2" + version: 5.1.3 + resolution: "vite-tsconfig-paths@npm:5.1.3" dependencies: debug: "npm:^4.1.1" globrex: "npm:^0.1.2" @@ -11980,7 +11950,7 @@ __metadata: peerDependenciesMeta: vite: optional: true - checksum: 10c0/7db445b6b1f48e7b89f39f5eb8cf4ea645994f581fcc7c9fac721e0c36f8203c0770007ec27825caa6e2566e3127b2b1bfe8be28ca05cd0e9fb67a2943dcdec5 + checksum: 10c0/fb7480efa31fd50439f4a12c91bc953e5cc09d69fdc7eeb6ffff7cc796bc2c1f2c617c3abfdcbf5d7414848076cea9deb60bc002142f93b6e3131e5458760710 languageName: node linkType: hard @@ -12145,11 +12115,11 @@ __metadata: linkType: hard "wagmi@npm:^2.12.25": - version: 2.12.32 - resolution: "wagmi@npm:2.12.32" + version: 2.13.0 + resolution: "wagmi@npm:2.13.0" dependencies: - "@wagmi/connectors": "npm:5.3.10" - "@wagmi/core": "npm:2.14.6" + "@wagmi/connectors": "npm:5.5.0" + "@wagmi/core": "npm:2.15.0" use-sync-external-store: "npm:1.2.0" peerDependencies: "@tanstack/react-query": ">=5.0.0" @@ -12159,7 +12129,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/eea7ac5ffedd83d65529591a0a80873b2bf04f93906a8f29fea7704970a8c7dd732871a13f2dfcaa5e34462c2d25038596eb782142d9493bc396e18d3adf6b01 + checksum: 10c0/fb3946009dca3e244c538ac72691bc263f95f1f6eef12f6720164b515adb8d3a9af063df773307f2206fa649d5821e4bb3d152bef734b4552a5360cf9237db80 languageName: node linkType: hard @@ -12487,9 +12457,9 @@ __metadata: linkType: hard "xxhash-wasm@npm:^1.0.2": - version: 1.0.2 - resolution: "xxhash-wasm@npm:1.0.2" - checksum: 10c0/5ba899d9216d9897de2d61a5331b16c99226e75ce47895fc8c730bac5cb00e6e50856dd8f489c12b3012f0fc81b6894806b2e44d2eb3cc7843919793485a30d1 + version: 1.1.0 + resolution: "xxhash-wasm@npm:1.1.0" + checksum: 10c0/35aa152fc7d775ae13364fe4fb20ebd89c6ac1f56cdb6060a6d2f1ed68d15180694467e63a4adb3d11936a4798ccd75a540979070e70d9b911e9981bbdd9cea6 languageName: node linkType: hard From 0537bd55d43c0e3bd7ced4132308824aa8fe72b5 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 13:46:44 +0700 Subject: [PATCH 65/84] Ledger signer working --- .../contexts/LedgerHardware/static/ledger.ts | 18 +++-- .../src/hooks/useSubmitExtrinsic/index.tsx | 29 ++++++- .../app/src/library/Signers/LedgerSigner.ts | 79 +++++++++++++++++++ .../SubmitTx/ManualSign/Ledger/Submit.tsx | 37 +-------- 4 files changed, 117 insertions(+), 46 deletions(-) create mode 100644 packages/app/src/library/Signers/LedgerSigner.ts diff --git a/packages/app/src/contexts/LedgerHardware/static/ledger.ts b/packages/app/src/contexts/LedgerHardware/static/ledger.ts index b9b18536c..3d7eca161 100644 --- a/packages/app/src/contexts/LedgerHardware/static/ledger.ts +++ b/packages/app/src/contexts/LedgerHardware/static/ledger.ts @@ -70,19 +70,21 @@ export class Ledger { static signPayload = async ( app: PolkadotGenericApp, index: number, - payload: AnyJson, - txMetadata: AnyJson + payload: Uint8Array, + txMetadata?: Uint8Array ) => { await this.ensureOpen(); const bip42Path = `m/44'/354'/${index}'/${0}'/${0}'`; - const buff = Buffer.from(txMetadata); + const toSign = Buffer.from(payload); - const result = await app.signWithMetadata( - bip42Path, - payload.toU8a(true), - buff - ); + let result; + if (txMetadata) { + const buff = Buffer.from(txMetadata); + result = await app.signWithMetadata(bip42Path, toSign, buff); + } else { + result = app.sign(bip42Path, toSign); + } await this.ensureClosed(); return result; diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index b413b04ad..279e69bbb 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -20,9 +20,13 @@ import { useProxySupported } from 'hooks/useProxySupported'; import { ApiController } from 'controllers/Api'; import { useNetwork } from 'contexts/Network'; import { useBalances } from 'contexts/Balances'; -import { InvalidTxError } from 'polkadot-api'; +import type { PolkadotSigner } from 'polkadot-api'; +import { AccountId, InvalidTxError } from 'polkadot-api'; import { connectInjectedExtension } from 'polkadot-api/pjs-signer'; import { formatAccountSs58 } from '@w3ux/utils'; +import { LedgerSigner } from 'library/Signers/LedgerSigner'; +import { getLedgerApp } from 'contexts/LedgerHardware/Utils'; +import type { LedgerAccount } from '@w3ux/react-connect-kit/types'; export const useSubmitExtrinsic = ({ tx, @@ -32,7 +36,10 @@ export const useSubmitExtrinsic = ({ callbackInBlock, }: UseSubmitExtrinsicProps): UseSubmitExtrinsic => { const { t } = useTranslation('library'); - const { network } = useNetwork(); + const { + network, + networkData: { units, unit }, + } = useNetwork(); const { getNonce } = useBalances(); const { activeProxy } = useActiveAccounts(); const { extensionsStatus } = useExtensions(); @@ -218,9 +225,23 @@ export const useSubmitExtrinsic = ({ setSubmitting(true); // handle signed transaction. - let signer; + let signer: PolkadotSigner | undefined; if (requiresManualSign(fromRef.current)) { - // TODO: Get custom signer here. + if (source === 'ledger') { + // Ledger signer. + signer = await new LedgerSigner( + AccountId().enc(fromRef.current), + getLedgerApp(network).txMetadataChainId + ).getPolkadotSigner( + { + decimals: units, + tokenSymbol: unit, + }, + (account as LedgerAccount).index + ); + } + + // TODO: Get Vault & Wallet Connect signers. } else { // Get the polkadot signer for this account. signer = (await connectInjectedExtension(source)) diff --git a/packages/app/src/library/Signers/LedgerSigner.ts b/packages/app/src/library/Signers/LedgerSigner.ts new file mode 100644 index 000000000..a1eca9c67 --- /dev/null +++ b/packages/app/src/library/Signers/LedgerSigner.ts @@ -0,0 +1,79 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata'; +import type { PolkadotSigner } from 'polkadot-api'; +import { mergeUint8 } from 'polkadot-api/utils'; +import type { V15 } from '@polkadot-api/substrate-bindings'; +import { decAnyMetadata } from '@polkadot-api/substrate-bindings'; +import { Ledger } from '../../contexts/LedgerHardware/static/ledger'; +import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; + +const CheckMetadataHash = 'CheckMetadataHash'; + +export class LedgerSigner { + #publicKey: Uint8Array; + #txMetadataChainId: string; + + constructor(pubKey: Uint8Array, txMetadataChainId: string) { + this.#publicKey = pubKey; + this.#txMetadataChainId = txMetadataChainId; + } + + async getPolkadotSigner( + networkInfo: { decimals: number; tokenSymbol: string }, + index: number + ): Promise { + const { app } = await Ledger.initialise(this.#txMetadataChainId); + + const signTx: PolkadotSigner['signTx'] = async ( + callData, + signedExtensions, + metadata + ) => { + const merkleizer = merkleizeMetadata(metadata, networkInfo); + const digest = merkleizer.digest(); + + // NOTE: Assuming metadata is version 15. Could introduce error here for `useSubmitExtrinsic` + // to handle. + const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; + + // NOTE: Assuming `CheckMetadataHash` signed extension exists in metadata. Could introduce + // error here for `useSubmitExtrinsic` to handle. + const extra: Uint8Array[] = []; + const additionalSigned: Uint8Array[] = []; + v15.extrinsic.signedExtensions.map(({ identifier }) => { + if (identifier === CheckMetadataHash) { + extra.push(Uint8Array.from([1])); + additionalSigned.push(mergeUint8(Uint8Array.from([1]), digest)); + return; + } + const signedExtension = signedExtensions[identifier]; + if (signedExtension) { + extra.push(signedExtension.value); + additionalSigned.push(signedExtension.additionalSigned); + } + }); + + const toSign = mergeUint8(callData, ...extra, ...additionalSigned); + const { signature } = await Ledger.signPayload( + app, + index, + toSign, + merkleizer.getProofForExtrinsicPayload(toSign) + ); + return createV4Tx(v15, this.#publicKey, signature, extra, callData); + }; + + return { + publicKey: this.#publicKey, + signTx, + signBytes: getSignBytes(async (x) => { + const { signature } = await Ledger.signPayload(app, index, x); + // NOTE: the signature includes a "0x00" at the beginning, indicating a ed25519 signature. + // this is not needed for non-extrinsic signatures. + return signature.subarray(1); + }), + }; + } +} diff --git a/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx b/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx index e6e2aeab9..a76e7e330 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Ledger/Submit.tsx @@ -3,9 +3,6 @@ import { faUsb } from '@fortawesome/free-brands-svg-icons'; import { faSquarePen } from '@fortawesome/free-solid-svg-icons'; -import type { LedgerAccount } from '@w3ux/react-connect-kit/types'; -import { useActiveAccounts } from 'contexts/ActiveAccounts'; -import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import { useLedgerHardware } from 'contexts/LedgerHardware'; import { getLedgerApp } from 'contexts/LedgerHardware/Utils'; import { useNetwork } from 'contexts/Network'; @@ -23,37 +20,11 @@ export const Submit = ({ disabled, }: LedgerSubmitProps) => { const { t } = useTranslation('library'); - const { - handleSignTx, - getIsExecuting, - integrityChecked, - checkRuntimeVersion, - } = useLedgerHardware(); const { network } = useNetwork(); const { getTxSignature } = useTxMeta(); - const { getAccount } = useImportedAccounts(); - const { activeAccount } = useActiveAccounts(); - const { getTxMetadata, getTxPayload, getPayloadUid } = useTxMeta(); const { txMetadataChainId } = getLedgerApp(network); - - const getAddressIndex = () => - (getAccount(activeAccount) as LedgerAccount)?.index || 0; - - // Handle transaction submission - const handleTxSubmit = async () => { - const uid = getPayloadUid(); - const accountIndex = getAddressIndex(); - const txMetadata = await getTxMetadata(); - const payload = await getTxPayload(); - - await handleSignTx( - txMetadataChainId, - uid, - accountIndex, - payload, - txMetadata - ); - }; + const { getIsExecuting, integrityChecked, checkRuntimeVersion } = + useLedgerHardware(); // Check device runtime version. const handleCheckRuntimeVersion = async () => { @@ -66,9 +37,7 @@ export const Submit = ({ // Button `onClick` handler depends whether integrityChecked and whether tx has been submitted. const handleOnClick = !integrityChecked ? handleCheckRuntimeVersion - : txReady - ? onSubmit - : handleTxSubmit; + : onSubmit; // Determine button text. const text = !integrityChecked From d604440ffb3acdbccf38835843628127d93c3e0c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 13:59:59 +0700 Subject: [PATCH 66/84] queries, apis at best --- .../src/canvas/JoinPool/Overview/Stats.tsx | 3 +- .../src/contexts/Pools/ActivePool/index.tsx | 6 ++- .../src/model/Entries/BondedPools/index.tsx | 10 ++-- .../app/src/model/Entries/Validators/index.ts | 2 +- .../app/src/model/ErasStakersPaged/index.tsx | 3 +- .../app/src/model/Query/BondedMulti/index.ts | 5 +- .../src/model/Query/ClaimedRewards/index.ts | 5 +- packages/app/src/model/Query/Era/index.ts | 4 +- .../src/model/Query/ErasRewardPoints/index.ts | 5 +- .../Query/ErasRewardPointsMulti/index.ts | 5 +- .../model/Query/ErasStakersOverview/index.tsx | 4 +- .../model/Query/ErasValidatorReward/index.ts | 4 +- .../Query/ErasValidatorRewardMulti/index.ts | 5 +- .../src/model/Query/IdentityOfMulti/index.ts | 5 +- .../app/src/model/Query/NetworkMeta/index.ts | 52 +++++++++++-------- .../src/model/Query/NominatorsMulti/index.ts | 3 +- .../model/Query/ParaSessionAccounts/index.ts | 3 +- .../model/Query/PoolMetadataMulti/index.ts | 3 +- .../app/src/model/Query/ProxiesQuery/index.ts | 3 +- .../model/Query/SessionValidators/index.ts | 4 +- .../app/src/model/Query/SuperOfMulti/index.ts | 3 +- .../src/model/Query/ValidatorPrefs/index.ts | 3 +- .../src/model/Query/ValidatorsMulti/index.ts | 3 +- .../model/Subscribe/AccountBalances/index.ts | 3 +- 24 files changed, 97 insertions(+), 49 deletions(-) diff --git a/packages/app/src/canvas/JoinPool/Overview/Stats.tsx b/packages/app/src/canvas/JoinPool/Overview/Stats.tsx index 4722836a0..7e461d8f1 100644 --- a/packages/app/src/canvas/JoinPool/Overview/Stats.tsx +++ b/packages/app/src/canvas/JoinPool/Overview/Stats.tsx @@ -50,7 +50,8 @@ export const Stats = ({ const apiResult = await pApi.apis.NominationPoolsApi.points_to_balance( bondedPool.id, - BigInt(rmCommas(bondedPool.points)) + BigInt(rmCommas(bondedPool.points)), + { at: 'best' } ); const balance = new BigNumber(apiResult?.toString() || 0); diff --git a/packages/app/src/contexts/Pools/ActivePool/index.tsx b/packages/app/src/contexts/Pools/ActivePool/index.tsx index f5209bba7..f8e535233 100644 --- a/packages/app/src/contexts/Pools/ActivePool/index.tsx +++ b/packages/app/src/contexts/Pools/ActivePool/index.tsx @@ -195,8 +195,10 @@ export const ActivePoolProvider = ({ children }: { children: ReactNode }) => { const fetchPendingRewards = async (address: string | undefined) => { const { pApi } = ApiController.get(network); if (pApi && address) { - const apiResult = - await pApi.apis.NominationPoolsApi.pending_rewards(address); + const apiResult = await pApi.apis.NominationPoolsApi.pending_rewards( + address, + { at: 'best' } + ); return new BigNumber(apiResult?.toString() || 0); } return new BigNumber(0); diff --git a/packages/app/src/model/Entries/BondedPools/index.tsx b/packages/app/src/model/Entries/BondedPools/index.tsx index 9fa767917..4f34b5600 100644 --- a/packages/app/src/model/Entries/BondedPools/index.tsx +++ b/packages/app/src/model/Entries/BondedPools/index.tsx @@ -17,13 +17,17 @@ export class BondedPools { async fetch() { this.bondedPools = - await this.#pApi.query.NominationPools.BondedPools.getEntries(); + await this.#pApi.query.NominationPools.BondedPools.getEntries({ + at: 'best', + }); return this; } async fetchOne(id: number) { - const result = - await this.#pApi.query.NominationPools.BondedPools.getValue(id); + const result = await this.#pApi.query.NominationPools.BondedPools.getValue( + id, + { at: 'best' } + ); if (!result) { return null; diff --git a/packages/app/src/model/Entries/Validators/index.ts b/packages/app/src/model/Entries/Validators/index.ts index 1f64955f5..b5eea430d 100644 --- a/packages/app/src/model/Entries/Validators/index.ts +++ b/packages/app/src/model/Entries/Validators/index.ts @@ -11,6 +11,6 @@ export class Validators { } async fetch() { - return await this.#pApi.query.Staking.Validators.getEntries(); + return await this.#pApi.query.Staking.Validators.getEntries({ at: 'best' }); } } diff --git a/packages/app/src/model/ErasStakersPaged/index.tsx b/packages/app/src/model/ErasStakersPaged/index.tsx index a7a3a57f3..144800800 100644 --- a/packages/app/src/model/ErasStakersPaged/index.tsx +++ b/packages/app/src/model/ErasStakersPaged/index.tsx @@ -13,7 +13,8 @@ export class ErasStakersPaged { async fetch(era: string, validator: string) { return await this.#pApi.query.Staking.ErasStakersPaged.getEntries( era, - validator + validator, + { at: 'best' } ); } } diff --git a/packages/app/src/model/Query/BondedMulti/index.ts b/packages/app/src/model/Query/BondedMulti/index.ts index d56d994d9..80fb032ad 100644 --- a/packages/app/src/model/Query/BondedMulti/index.ts +++ b/packages/app/src/model/Query/BondedMulti/index.ts @@ -16,7 +16,10 @@ export class BondedMulti { async fetch() { try { const results = await this.#pApi.query.Staking.Bonded.getValues( - this.#addresses + this.#addresses, + { + at: 'best', + } ); return results; } catch (e) { diff --git a/packages/app/src/model/Query/ClaimedRewards/index.ts b/packages/app/src/model/Query/ClaimedRewards/index.ts index 35a90cfdf..6fabb0332 100644 --- a/packages/app/src/model/Query/ClaimedRewards/index.ts +++ b/packages/app/src/model/Query/ClaimedRewards/index.ts @@ -20,7 +20,10 @@ export class ClaimedRewards { try { const result = await this.#pApi.query.Staking.ClaimedRewards.getValue( this.#era, - this.#address + this.#address, + { + at: 'best', + } ); return result; } catch (e) { diff --git a/packages/app/src/model/Query/Era/index.ts b/packages/app/src/model/Query/Era/index.ts index d572c27f5..5100ced58 100644 --- a/packages/app/src/model/Query/Era/index.ts +++ b/packages/app/src/model/Query/Era/index.ts @@ -15,7 +15,9 @@ export class Era { let result; try { const { index, start } = - await this.#pApi.query.Staking.ActiveEra.getValue(); + await this.#pApi.query.Staking.ActiveEra.getValue({ + at: 'best', + }); result = { start, diff --git a/packages/app/src/model/Query/ErasRewardPoints/index.ts b/packages/app/src/model/Query/ErasRewardPoints/index.ts index 149910abb..6ac3b3075 100644 --- a/packages/app/src/model/Query/ErasRewardPoints/index.ts +++ b/packages/app/src/model/Query/ErasRewardPoints/index.ts @@ -16,7 +16,10 @@ export class ErasRewardPoints { async fetch() { try { const result = await this.#pApi.query.Staking.ErasRewardPoints.getValue( - this.#era + this.#era, + { + at: 'best', + } ); return result; } catch (e) { diff --git a/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts index 14ac6a518..2c02adcc1 100644 --- a/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts +++ b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts @@ -16,7 +16,10 @@ export class ErasRewardPointsMulti { async fetch() { try { const results = await this.#pApi.query.Staking.ErasRewardPoints.getValues( - this.#eras + this.#eras, + { + at: 'best', + } ); return results; } catch (e) { diff --git a/packages/app/src/model/Query/ErasStakersOverview/index.tsx b/packages/app/src/model/Query/ErasStakersOverview/index.tsx index 8c42b5d3a..cae72128e 100644 --- a/packages/app/src/model/Query/ErasStakersOverview/index.tsx +++ b/packages/app/src/model/Query/ErasStakersOverview/index.tsx @@ -11,6 +11,8 @@ export class ErasStakersOverview { } async fetch(era: string) { - return await this.#pApi.query.Staking.ErasStakersOverview.getEntries(era); + return await this.#pApi.query.Staking.ErasStakersOverview.getEntries(era, { + at: 'best', + }); } } diff --git a/packages/app/src/model/Query/ErasValidatorReward/index.ts b/packages/app/src/model/Query/ErasValidatorReward/index.ts index fc6a9b9bf..bcdc54daf 100644 --- a/packages/app/src/model/Query/ErasValidatorReward/index.ts +++ b/packages/app/src/model/Query/ErasValidatorReward/index.ts @@ -16,7 +16,9 @@ export class ErasValidatorReward { async fetch() { try { const result = - await this.#pApi.query.Staking.ErasValidatorReward.getValue(this.#era); + await this.#pApi.query.Staking.ErasValidatorReward.getValue(this.#era, { + at: 'best', + }); return result; } catch (e) { // Silently fail. diff --git a/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts index 61894f746..f57bc15e0 100644 --- a/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts +++ b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts @@ -17,7 +17,10 @@ export class ErasValidatorReward { try { const results = await this.#pApi.query.Staking.ErasValidatorReward.getValues( - this.#eras + this.#eras, + { + at: 'best', + } ); return results; } catch (e) { diff --git a/packages/app/src/model/Query/IdentityOfMulti/index.ts b/packages/app/src/model/Query/IdentityOfMulti/index.ts index 19ea4332e..f96df081d 100644 --- a/packages/app/src/model/Query/IdentityOfMulti/index.ts +++ b/packages/app/src/model/Query/IdentityOfMulti/index.ts @@ -16,7 +16,10 @@ export class IdentityOfMulti { async fetch() { try { const result = await this.#pApi.query.Identity.IdentityOf.getValues( - this.#addresses + this.#addresses, + { + at: 'best', + } ); return result; } catch (e) { diff --git a/packages/app/src/model/Query/NetworkMeta/index.ts b/packages/app/src/model/Query/NetworkMeta/index.ts index f8e99dc2b..80b48eabe 100644 --- a/packages/app/src/model/Query/NetworkMeta/index.ts +++ b/packages/app/src/model/Query/NetworkMeta/index.ts @@ -15,8 +15,9 @@ export class NetworkMeta { // Fetch network constants. async fetch(activeEra: APIActiveEra, previousEra: BigNumber) { + const at = { at: 'best' }; const totalIssuance = - await this.#pApi.query.Balances.TotalIssuance.getValue(); + await this.#pApi.query.Balances.TotalIssuance.getValue(at); const [ auctionCounter, @@ -42,31 +43,36 @@ export class NetworkMeta { minNominatorBond, activeEraErasTotalStake, ] = await Promise.all([ - this.#pApi.query.Auctions.AuctionCounter.getValue(), - this.#pApi.query.ParaSessionInfo.EarliestStoredSession.getValue(), - this.#pApi.query.FastUnstake.ErasToCheckPerBlock.getValue(), - this.#pApi.query.Staking.MinimumActiveStake.getValue(), - this.#pApi.query.NominationPools.CounterForPoolMembers.getValue(), - this.#pApi.query.NominationPools.CounterForBondedPools.getValue(), - this.#pApi.query.NominationPools.CounterForRewardPools.getValue(), - this.#pApi.query.NominationPools.LastPoolId.getValue(), - this.#pApi.query.NominationPools.MaxPoolMembers.getValue(), - this.#pApi.query.NominationPools.MaxPoolMembersPerPool.getValue(), - this.#pApi.query.NominationPools.MaxPools.getValue(), - this.#pApi.query.NominationPools.MinCreateBond.getValue(), - this.#pApi.query.NominationPools.MinJoinBond.getValue(), - this.#pApi.query.NominationPools.GlobalMaxCommission.getValue(), - this.#pApi.query.Staking.CounterForNominators.getValue(), - this.#pApi.query.Staking.CounterForValidators.getValue(), - this.#pApi.query.Staking.MaxValidatorsCount.getValue(), - this.#pApi.query.Staking.ValidatorCount.getValue(), + this.#pApi.query.Auctions.AuctionCounter.getValue(at), + this.#pApi.query.ParaSessionInfo.EarliestStoredSession.getValue(at), + this.#pApi.query.FastUnstake.ErasToCheckPerBlock.getValue(at), + this.#pApi.query.Staking.MinimumActiveStake.getValue(at), + this.#pApi.query.NominationPools.CounterForPoolMembers.getValue(at), + this.#pApi.query.NominationPools.CounterForBondedPools.getValue(at), + this.#pApi.query.NominationPools.CounterForRewardPools.getValue(at), + this.#pApi.query.NominationPools.LastPoolId.getValue(at), + this.#pApi.query.NominationPools.MaxPoolMembers.getValue(at), + this.#pApi.query.NominationPools.MaxPoolMembersPerPool.getValue(at), + this.#pApi.query.NominationPools.MaxPools.getValue(at), + this.#pApi.query.NominationPools.MinCreateBond.getValue(at), + this.#pApi.query.NominationPools.MinJoinBond.getValue(at), + this.#pApi.query.NominationPools.GlobalMaxCommission.getValue(at), + this.#pApi.query.Staking.CounterForNominators.getValue(at), + this.#pApi.query.Staking.CounterForValidators.getValue(at), + this.#pApi.query.Staking.MaxValidatorsCount.getValue(at), + this.#pApi.query.Staking.ValidatorCount.getValue(at), this.#pApi.query.Staking.ErasValidatorReward.getValue( - previousEra.toString() + previousEra.toString(), + at ), - this.#pApi.query.Staking.ErasTotalStake.getValue(previousEra.toString()), - this.#pApi.query.Staking.MinNominatorBond.getValue(), this.#pApi.query.Staking.ErasTotalStake.getValue( - activeEra.index.toString() + previousEra.toString(), + at + ), + this.#pApi.query.Staking.MinNominatorBond.getValue(at), + this.#pApi.query.Staking.ErasTotalStake.getValue( + activeEra.index.toString(), + at ), ]); diff --git a/packages/app/src/model/Query/NominatorsMulti/index.ts b/packages/app/src/model/Query/NominatorsMulti/index.ts index c9279c596..7f93d4e79 100644 --- a/packages/app/src/model/Query/NominatorsMulti/index.ts +++ b/packages/app/src/model/Query/NominatorsMulti/index.ts @@ -17,7 +17,8 @@ export class NominatorsMulti { let result; try { result = await this.#pApi.query.Staking.Nominators.getValues( - this.#addresses + this.#addresses, + { at: 'best' } ); return result.map((nominator) => { diff --git a/packages/app/src/model/Query/ParaSessionAccounts/index.ts b/packages/app/src/model/Query/ParaSessionAccounts/index.ts index 76ff767f7..1aedab661 100644 --- a/packages/app/src/model/Query/ParaSessionAccounts/index.ts +++ b/packages/app/src/model/Query/ParaSessionAccounts/index.ts @@ -17,7 +17,8 @@ export class ParaSessionAccounts { try { const result = await this.#pApi.query.ParaSessionInfo.AccountKeys.getValue( - this.#session + this.#session, + { at: 'best' } ); return result; } catch (e) { diff --git a/packages/app/src/model/Query/PoolMetadataMulti/index.ts b/packages/app/src/model/Query/PoolMetadataMulti/index.ts index e28220f5b..59f9c836f 100644 --- a/packages/app/src/model/Query/PoolMetadataMulti/index.ts +++ b/packages/app/src/model/Query/PoolMetadataMulti/index.ts @@ -16,7 +16,8 @@ export class PoolMetadataMulti { async fetch() { try { const result = await this.#pApi.query.NominationPools.Metadata.getValues( - this.#ids + this.#ids, + { at: 'best' } ); return result.map((metadata) => metadata.asText()); } catch (e) { diff --git a/packages/app/src/model/Query/ProxiesQuery/index.ts b/packages/app/src/model/Query/ProxiesQuery/index.ts index 49866d57b..2237c02d5 100644 --- a/packages/app/src/model/Query/ProxiesQuery/index.ts +++ b/packages/app/src/model/Query/ProxiesQuery/index.ts @@ -16,7 +16,8 @@ export class ProxiesQuery { async fetch() { try { const result = await this.#pApi.query.Proxy.Proxies.getValue( - this.#address + this.#address, + { at: 'best' } ); return result; } catch (e) { diff --git a/packages/app/src/model/Query/SessionValidators/index.ts b/packages/app/src/model/Query/SessionValidators/index.ts index 6b35d998c..011d08f87 100644 --- a/packages/app/src/model/Query/SessionValidators/index.ts +++ b/packages/app/src/model/Query/SessionValidators/index.ts @@ -13,7 +13,9 @@ export class SessionValidators { // Fetch network constants. async fetch() { try { - const result = await this.#pApi.query.Session.Validators.getValue(); + const result = await this.#pApi.query.Session.Validators.getValue({ + at: 'best', + }); return result; } catch (e) { // Silently fail. diff --git a/packages/app/src/model/Query/SuperOfMulti/index.ts b/packages/app/src/model/Query/SuperOfMulti/index.ts index 3ae9eb11c..6f1c1d8f8 100644 --- a/packages/app/src/model/Query/SuperOfMulti/index.ts +++ b/packages/app/src/model/Query/SuperOfMulti/index.ts @@ -16,7 +16,8 @@ export class SuperOfMulti { async fetch() { try { const result = await this.#pApi.query.Identity.SuperOf.getValues( - this.#addresses + this.#addresses, + { at: 'best' } ); return result; } catch (e) { diff --git a/packages/app/src/model/Query/ValidatorPrefs/index.ts b/packages/app/src/model/Query/ValidatorPrefs/index.ts index 9554a8246..086a61949 100644 --- a/packages/app/src/model/Query/ValidatorPrefs/index.ts +++ b/packages/app/src/model/Query/ValidatorPrefs/index.ts @@ -20,7 +20,8 @@ export class ValidatorPrefs { try { const result = await this.#pApi.query.Staking.ErasValidatorPrefs.getValue( this.#era, - this.#address + this.#address, + { at: 'best' } ); return result; } catch (e) { diff --git a/packages/app/src/model/Query/ValidatorsMulti/index.ts b/packages/app/src/model/Query/ValidatorsMulti/index.ts index 66c17bc8d..414037ecc 100644 --- a/packages/app/src/model/Query/ValidatorsMulti/index.ts +++ b/packages/app/src/model/Query/ValidatorsMulti/index.ts @@ -16,7 +16,8 @@ export class ValidatorsMulti { async fetch() { try { const result = await this.#pApi.query.Staking.Validators.getValues( - this.#addresses + this.#addresses, + { at: 'best' } ); return result; } catch (e) { diff --git a/packages/app/src/model/Subscribe/AccountBalances/index.ts b/packages/app/src/model/Subscribe/AccountBalances/index.ts index 612124f6a..eaf52d1c9 100644 --- a/packages/app/src/model/Subscribe/AccountBalances/index.ts +++ b/packages/app/src/model/Subscribe/AccountBalances/index.ts @@ -196,7 +196,8 @@ export class AccountBalances implements Unsubscribable { const apiResult = await pApi.apis.NominationPoolsApi.points_to_balance( poolMembers.pool_id, - poolMembers.points + poolMembers.points, + { at: 'best' } ); const balance = new BigNumber(apiResult?.toString() || 0); const claimPermission = claimPermissionResult?.type || 'Permissioned'; From 853e5b97b620da01ad401445073c88ad675dab77 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 21 Nov 2024 17:38:43 +0700 Subject: [PATCH 67/84] some vault boilerplate --- .../src/hooks/useSubmitExtrinsic/index.tsx | 54 +++++++--- .../app/src/library/QRCode/DisplayPayload.tsx | 18 ++-- .../app/src/library/Signers/LedgerSigner.ts | 29 ++---- .../app/src/library/Signers/VaultSigner.ts | 98 +++++++++++++++++++ packages/app/src/library/Signers/util.ts | 43 ++++++++ .../SubmitTx/ManualSign/Vault/SignPrompt.tsx | 32 +++--- .../SubmitTx/ManualSign/Vault/index.tsx | 18 ++-- packages/app/src/library/SubmitTx/types.ts | 2 + 8 files changed, 222 insertions(+), 72 deletions(-) create mode 100644 packages/app/src/library/Signers/VaultSigner.ts create mode 100644 packages/app/src/library/Signers/util.ts diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 279e69bbb..4ddebe6af 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -27,6 +27,9 @@ import { formatAccountSs58 } from '@w3ux/utils'; import { LedgerSigner } from 'library/Signers/LedgerSigner'; import { getLedgerApp } from 'contexts/LedgerHardware/Utils'; import type { LedgerAccount } from '@w3ux/react-connect-kit/types'; +import { VaultSigner } from 'library/Signers/VaultSigner'; +import { usePrompt } from 'contexts/Prompt'; +import { SignPrompt } from 'library/SubmitTx/ManualSign/Vault/SignPrompt'; export const useSubmitExtrinsic = ({ tx, @@ -44,6 +47,7 @@ export const useSubmitExtrinsic = ({ const { activeProxy } = useActiveAccounts(); const { extensionsStatus } = useExtensions(); const { isProxySupported } = useProxySupported(); + const { openPromptWith, closePrompt } = usePrompt(); const { handleResetLedgerTask } = useLedgerHardware(); const { addPendingNonce, removePendingNonce } = useTxMeta(); const { getAccount, requiresManualSign } = useImportedAccounts(); @@ -227,21 +231,43 @@ export const useSubmitExtrinsic = ({ // handle signed transaction. let signer: PolkadotSigner | undefined; if (requiresManualSign(fromRef.current)) { - if (source === 'ledger') { - // Ledger signer. - signer = await new LedgerSigner( - AccountId().enc(fromRef.current), - getLedgerApp(network).txMetadataChainId - ).getPolkadotSigner( - { - decimals: units, - tokenSymbol: unit, - }, - (account as LedgerAccount).index - ); + const pubKey = AccountId().enc(fromRef.current); + const networkInfo = { + decimals: units, + tokenSymbol: unit, + }; + + switch (source) { + case 'ledger': + signer = await new LedgerSigner( + pubKey, + getLedgerApp(network).txMetadataChainId + ).getPolkadotSigner(networkInfo, (account as LedgerAccount).index); + break; + + case 'vault': + signer = await new VaultSigner(pubKey, { + openPrompt: ( + onComplete: (result: Uint8Array) => void, + toSign: Uint8Array + ) => { + openPromptWith( + , + 'small' + ); + }, + closePrompt: () => closePrompt(), + }).getPolkadotSigner(networkInfo); + break; + + case 'wallet_connect': + // TODO: Implement + break; } - - // TODO: Get Vault & Wallet Connect signers. } else { // Get the polkadot signer for this account. signer = (await connectInjectedExtension(source)) diff --git a/packages/app/src/library/QRCode/DisplayPayload.tsx b/packages/app/src/library/QRCode/DisplayPayload.tsx index 3fcf5f5fa..d4b45f689 100644 --- a/packages/app/src/library/QRCode/DisplayPayload.tsx +++ b/packages/app/src/library/QRCode/DisplayPayload.tsx @@ -5,27 +5,23 @@ import type { ReactElement } from 'react'; import { memo, useMemo } from 'react'; import { QrDisplay } from './Display.js'; import type { DisplayPayloadProps } from './types.js'; -import { u8aConcat } from '@polkadot/util'; -import { decodeAddress } from '@polkadot/util-crypto'; +import { AccountId } from 'polkadot-api'; +import { mergeUint8 } from 'polkadot-api/utils'; const createSignPayload = ( address: string, cmd: number, payload: Uint8Array, genesisHash: Uint8Array -): Uint8Array => { - const SUBSTRATE_ID = new Uint8Array([0x53]); - const CRYPTO_SR25519 = new Uint8Array([0x01]); - - return u8aConcat( - SUBSTRATE_ID, - CRYPTO_SR25519, +): Uint8Array => + mergeUint8( + new Uint8Array([0x53]), // SUBSTRATE_ID + new Uint8Array([0x01]), // CRYPTO_SR25519 new Uint8Array([cmd]), - decodeAddress(address), + AccountId().enc(address), payload, genesisHash ); -}; const DisplayPayload = ({ address, diff --git a/packages/app/src/library/Signers/LedgerSigner.ts b/packages/app/src/library/Signers/LedgerSigner.ts index a1eca9c67..ba544d70c 100644 --- a/packages/app/src/library/Signers/LedgerSigner.ts +++ b/packages/app/src/library/Signers/LedgerSigner.ts @@ -8,8 +8,7 @@ import type { V15 } from '@polkadot-api/substrate-bindings'; import { decAnyMetadata } from '@polkadot-api/substrate-bindings'; import { Ledger } from '../../contexts/LedgerHardware/static/ledger'; import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; - -const CheckMetadataHash = 'CheckMetadataHash'; +import { getExtraSignedExtensions } from './util'; export class LedgerSigner { #publicKey: Uint8Array; @@ -33,27 +32,13 @@ export class LedgerSigner { ) => { const merkleizer = merkleizeMetadata(metadata, networkInfo); const digest = merkleizer.digest(); - - // NOTE: Assuming metadata is version 15. Could introduce error here for `useSubmitExtrinsic` - // to handle. const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; - - // NOTE: Assuming `CheckMetadataHash` signed extension exists in metadata. Could introduce - // error here for `useSubmitExtrinsic` to handle. - const extra: Uint8Array[] = []; - const additionalSigned: Uint8Array[] = []; - v15.extrinsic.signedExtensions.map(({ identifier }) => { - if (identifier === CheckMetadataHash) { - extra.push(Uint8Array.from([1])); - additionalSigned.push(mergeUint8(Uint8Array.from([1]), digest)); - return; - } - const signedExtension = signedExtensions[identifier]; - if (signedExtension) { - extra.push(signedExtension.value); - additionalSigned.push(signedExtension.additionalSigned); - } - }); + const { extra, additionalSigned } = getExtraSignedExtensions( + v15, + digest, + signedExtensions, + true + ); const toSign = mergeUint8(callData, ...extra, ...additionalSigned); const { signature } = await Ledger.signPayload( diff --git a/packages/app/src/library/Signers/VaultSigner.ts b/packages/app/src/library/Signers/VaultSigner.ts new file mode 100644 index 000000000..96998dce5 --- /dev/null +++ b/packages/app/src/library/Signers/VaultSigner.ts @@ -0,0 +1,98 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; +import type { V15 } from '@polkadot-api/substrate-bindings'; +import { decAnyMetadata } from '@polkadot-api/substrate-bindings'; +import type { PolkadotSigner } from 'polkadot-api'; +import { mergeUint8 } from 'polkadot-api/utils'; +import type { AnyApi } from 'types'; + +export class VaultSigner { + #publicKey: Uint8Array; + #promptHandlers: { + openPrompt: ( + onComplete: (result: Uint8Array) => void, + toSign: Uint8Array + ) => void; + closePrompt: () => void; + }; + + constructor( + pubKey: Uint8Array, + promptHandlers: { openPrompt: AnyApi; closePrompt: AnyApi } + ) { + this.#publicKey = pubKey; + this.#promptHandlers = promptHandlers; + } + + #showPrompt = (toSign: Uint8Array) => + new Promise((resolve) => { + const handleComplete = (result: Uint8Array) => { + this.#promptHandlers.closePrompt(); + resolve(result); + }; + // Show prompt, passing completion handler to call once user has completed signing. This will + // resolve the promise and continue with signing. + this.#promptHandlers.openPrompt( + (result: Uint8Array) => handleComplete(result), + toSign + ); + }); + + async getPolkadotSigner(networkInfo: { + decimals: number; + tokenSymbol: string; + }): Promise { + const signTx: PolkadotSigner['signTx'] = async ( + callData, + signedExtensions, + metadata + ) => { + console.debug(networkInfo); + // const merkleizer = merkleizeMetadata(metadata, networkInfo); + // const digest = merkleizer.digest(); + const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; + + const extra: Uint8Array[] = []; + const additionalSigned: Uint8Array[] = []; + v15.extrinsic.signedExtensions.map(({ identifier }) => { + const signedExtension = signedExtensions[identifier]; + if (!signedExtension) { + throw new Error(`Missing ${identifier} signed extension`); + } + extra.push(signedExtension.value); + additionalSigned.push(signedExtension.additionalSigned); + }); + + const prefix = new Uint8Array([8]); + const toSign = mergeUint8( + prefix, + callData, + ...extra, + ...additionalSigned + ); + + // Start flow to sign QR Code here. + const userResponse = await this.#showPrompt(toSign); + + console.log('user response: ', userResponse); + // NOTE: Placeholder. + const signature = Buffer.from(new Uint8Array(0)); + return createV4Tx(v15, this.#publicKey, signature, extra, callData); + }; + + return { + publicKey: this.#publicKey, + signTx, + signBytes: getSignBytes(async (x) => { + const signature = Buffer.from(x); + // TODO: Replace with vault signature. + // const { signature } = await Ledger.signPayload(app, index, x); + // NOTE: the signature includes a "0x00" at the beginning, indicating a ed25519 signature. + // this is not needed for non-extrinsic signatures. + return signature.subarray(1); + }), + }; + } +} diff --git a/packages/app/src/library/Signers/util.ts b/packages/app/src/library/Signers/util.ts new file mode 100644 index 000000000..f1ff80394 --- /dev/null +++ b/packages/app/src/library/Signers/util.ts @@ -0,0 +1,43 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import type { V15 } from '@polkadot-api/substrate-bindings'; +import { mergeUint8 } from 'polkadot-api/utils'; + +const CheckMetadataHash = 'CheckMetadataHash'; + +export const getExtraSignedExtensions = ( + v15: V15, + digest: Uint8Array, + signedExtensions: Record< + string, + { + identifier: string; + value: Uint8Array; + additionalSigned: Uint8Array; + } + >, + checkMetadataHash = false +): { + extra: Uint8Array[]; + additionalSigned: Uint8Array[]; +} => { + // NOTE: Assuming `CheckMetadataHash` signed extension exists in metadata. Could introduce + // error here for `useSubmitExtrinsic` to handle. + const extra: Uint8Array[] = []; + const additionalSigned: Uint8Array[] = []; + v15.extrinsic.signedExtensions.map(({ identifier }) => { + if (checkMetadataHash && identifier === CheckMetadataHash) { + extra.push(Uint8Array.from([1])); + additionalSigned.push(mergeUint8(Uint8Array.from([1]), digest)); + return; + } + const signedExtension = signedExtensions[identifier]; + if (signedExtension) { + extra.push(signedExtension.value); + additionalSigned.push(signedExtension.additionalSigned); + } + }); + + return { extra, additionalSigned }; +}; diff --git a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx index 984b50016..3f9d6de90 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx @@ -8,22 +8,25 @@ import { import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { usePrompt } from 'contexts/Prompt'; -import { useTxMeta } from 'contexts/TxMeta'; import { QRViewerWrapper } from 'library/Import/Wrappers'; import { QrDisplayPayload } from 'library/QRCode/DisplayPayload'; import { QrScanSignature } from 'library/QRCode/ScanSignature'; import type { SignerPromptProps } from 'library/SubmitTx/types'; import type { AnyJson } from '@w3ux/types'; import { ButtonPrimary, ButtonSecondary } from 'ui-buttons'; +import { Bytes } from '@polkadot-api/substrate-bindings'; +import { useApi } from 'contexts/Api'; -export const SignPrompt = ({ submitAddress }: SignerPromptProps) => { - const { t } = useTranslation('library'); - const { getTxPayload, setTxSignature } = useTxMeta(); +export const SignPrompt = ({ + submitAddress, + toSign, + onComplete, +}: SignerPromptProps) => { + const { + chainSpecs: { genesisHash }, + } = useApi(); - const payload = getTxPayload(); - const payloadU8a = payload?.toU8a(); - const { closePrompt } = usePrompt(); + const { t } = useTranslation('library'); // Whether user is on sign or submit stage. const [stage, setStage] = useState(1); @@ -47,8 +50,8 @@ export const SignPrompt = ({ submitAddress }: SignerPromptProps) => { @@ -58,8 +61,8 @@ export const SignPrompt = ({ submitAddress }: SignerPromptProps) => { { - closePrompt(); - setTxSignature(signature); + // TODO: Expand with result `status` of cancelled or complete. + onComplete(signature); }} /> @@ -90,7 +93,10 @@ export const SignPrompt = ({ submitAddress }: SignerPromptProps) => { text={t('cancel')} lg marginLeft - onClick={() => closePrompt()} + onClick={() => { + // TODO: Expand with result `status` of cancelled or complete. + onComplete(new Uint8Array(0)); + }} /> diff --git a/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx b/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx index 4ebf1f488..bb4c38fdc 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Vault/index.tsx @@ -9,7 +9,6 @@ import { useTxMeta } from 'contexts/TxMeta'; import { EstimatedTxFee } from 'library/EstimatedTxFee'; import { useImportedAccounts } from 'contexts/Connect/ImportedAccounts'; import type { SubmitProps } from '../../types'; -import { SignPrompt } from './SignPrompt'; import { ButtonSubmit } from 'ui-buttons'; import { ButtonSubmitLarge } from 'library/SubmitTx/ButtonSubmitLarge'; import { appendOrEmpty } from '@w3ux/utils'; @@ -24,9 +23,9 @@ export const Vault = ({ displayFor, }: SubmitProps & { buttons?: ReactNode[] }) => { const { t } = useTranslation('library'); + const { txFeesValid } = useTxMeta(); + const { status: promptStatus } = usePrompt(); const { accountHasSigner } = useImportedAccounts(); - const { txFeesValid, getTxSignature } = useTxMeta(); - const { openPromptWith, status: promptStatus } = usePrompt(); // The state under which submission is disabled. const disabled = @@ -34,20 +33,15 @@ export const Vault = ({ // Format submit button based on whether signature currently exists or submission is ongoing. let buttonText: string; - let buttonOnClick: () => void; let buttonDisabled: boolean; let buttonPulse: boolean; - if (getTxSignature() !== null || submitting) { + if (submitting) { buttonText = submitText || ''; - buttonOnClick = onSubmit; buttonDisabled = disabled; buttonPulse = !(!valid || promptStatus !== 0); } else { - buttonText = promptStatus === 0 ? t('sign') : t('signing'); - buttonOnClick = async () => { - openPromptWith(, 'small'); - }; + buttonText = t('sign'); buttonDisabled = disabled || promptStatus !== 0; buttonPulse = !disabled || promptStatus === 0; } @@ -67,14 +61,14 @@ export const Vault = ({ text={buttonText} iconLeft={faSquarePen} iconTransform="grow-2" - onClick={() => buttonOnClick()} + onClick={() => onSubmit()} pulse={buttonPulse} /> ) : ( diff --git a/packages/app/src/library/SubmitTx/types.ts b/packages/app/src/library/SubmitTx/types.ts index 353c7f5f1..f47868302 100644 --- a/packages/app/src/library/SubmitTx/types.ts +++ b/packages/app/src/library/SubmitTx/types.ts @@ -26,6 +26,8 @@ export interface SubmitProps { export interface SignerPromptProps { submitAddress: MaybeAddress; + toSign: Uint8Array; + onComplete: (result: Uint8Array) => void; } export interface LedgerSubmitProps { From 0c86a59bb7ac3365d7ddfd36c52b3f10d160289f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 11:26:04 +0700 Subject: [PATCH 68/84] vault prompt behaviour working --- packages/app/src/contexts/Prompt/defaults.tsx | 4 +- packages/app/src/contexts/Prompt/index.tsx | 12 +++++- packages/app/src/contexts/Prompt/types.ts | 8 +++- .../src/hooks/useSubmitExtrinsic/index.tsx | 7 +++- packages/app/src/library/Prompt/index.tsx | 18 ++++++++- .../{VaultSigner.ts => VaultSigner/index.ts} | 37 +++++++++---------- .../src/library/Signers/VaultSigner/types.ts | 13 +++++++ .../SubmitTx/ManualSign/Vault/SignPrompt.tsx | 7 +--- packages/app/src/library/SubmitTx/types.ts | 2 +- 9 files changed, 76 insertions(+), 32 deletions(-) rename packages/app/src/library/Signers/{VaultSigner.ts => VaultSigner/index.ts} (79%) create mode 100644 packages/app/src/library/Signers/VaultSigner/types.ts diff --git a/packages/app/src/contexts/Prompt/defaults.tsx b/packages/app/src/contexts/Prompt/defaults.tsx index 097ec5ad4..57cdefa93 100644 --- a/packages/app/src/contexts/Prompt/defaults.tsx +++ b/packages/app/src/contexts/Prompt/defaults.tsx @@ -6,11 +6,13 @@ import type { PromptContextInterface } from './types'; export const defaultPromptContext: PromptContextInterface = { setOnClosePrompt: (value) => {}, - openPromptWith: (o, s) => {}, + openPromptWith: (o, s, c) => {}, closePrompt: () => {}, setStatus: (s) => {}, setPrompt: (d) => {}, + closeOnOutsideClick: true, size: 'small', status: 0, Prompt: null, + setCloseOnOutsideClick: (canClose) => {}, }; diff --git a/packages/app/src/contexts/Prompt/index.tsx b/packages/app/src/contexts/Prompt/index.tsx index 6f43db4b8..05c492c07 100644 --- a/packages/app/src/contexts/Prompt/index.tsx +++ b/packages/app/src/contexts/Prompt/index.tsx @@ -19,6 +19,9 @@ export const PromptProvider = ({ children }: { children: ReactNode }) => { onClosePrompt: null, }); + // Whether prompt can be closed by clicking outside on container. + const [closeOnOutsideClick, setCloseOnOutsideClick] = useState(false); + const setPrompt = (Prompt: Prompt) => { setState({ ...state, @@ -33,13 +36,18 @@ export const PromptProvider = ({ children }: { children: ReactNode }) => { }); }; - const openPromptWith = (Prompt: Prompt, size = 'small') => { + const openPromptWith = ( + Prompt: Prompt, + size = 'small', + closeOutside = true + ) => { setState({ ...state, size, Prompt, status: 1, }); + setCloseOnOutsideClick(closeOutside); }; const closePrompt = () => { @@ -70,9 +78,11 @@ export const PromptProvider = ({ children }: { children: ReactNode }) => { closePrompt, setStatus, setPrompt, + setCloseOnOutsideClick, size: state.size, status: state.status, Prompt: state.Prompt, + closeOnOutsideClick, }} > {children} diff --git a/packages/app/src/contexts/Prompt/types.ts b/packages/app/src/contexts/Prompt/types.ts index 05869612c..c6b4f797d 100644 --- a/packages/app/src/contexts/Prompt/types.ts +++ b/packages/app/src/contexts/Prompt/types.ts @@ -6,13 +6,19 @@ import type { MaybeString } from 'types'; export interface PromptContextInterface { setOnClosePrompt: (onClosePrompt: (() => void) | null) => void; - openPromptWith: (o: ReactNode | null, s?: string) => void; + openPromptWith: ( + o: ReactNode | null, + s?: string, + closeOnOutsideClick?: boolean + ) => void; closePrompt: () => void; setStatus: (s: number) => void; setPrompt: (d: MaybeString) => void; size: string; status: number; Prompt: Prompt; + closeOnOutsideClick: boolean; + setCloseOnOutsideClick: (canClose: boolean) => void; } export interface PromptState { diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 4ddebe6af..8e30bac78 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -30,6 +30,7 @@ import type { LedgerAccount } from '@w3ux/react-connect-kit/types'; import { VaultSigner } from 'library/Signers/VaultSigner'; import { usePrompt } from 'contexts/Prompt'; import { SignPrompt } from 'library/SubmitTx/ManualSign/Vault/SignPrompt'; +import type { VaultSignStatus } from 'library/Signers/VaultSigner/types'; export const useSubmitExtrinsic = ({ tx, @@ -248,7 +249,7 @@ export const useSubmitExtrinsic = ({ case 'vault': signer = await new VaultSigner(pubKey, { openPrompt: ( - onComplete: (result: Uint8Array) => void, + onComplete: (status: VaultSignStatus, result: Uint8Array) => void, toSign: Uint8Array ) => { openPromptWith( @@ -257,10 +258,12 @@ export const useSubmitExtrinsic = ({ onComplete={onComplete} toSign={toSign} />, - 'small' + 'small', + false ); }, closePrompt: () => closePrompt(), + setSubmitting, }).getPolkadotSigner(networkInfo); break; diff --git a/packages/app/src/library/Prompt/index.tsx b/packages/app/src/library/Prompt/index.tsx index 256ffec57..ab68b535d 100644 --- a/packages/app/src/library/Prompt/index.tsx +++ b/packages/app/src/library/Prompt/index.tsx @@ -5,7 +5,13 @@ import { usePrompt } from 'contexts/Prompt'; import { ContentWrapper, HeightWrapper, PromptWrapper } from './Wrappers'; export const Prompt = () => { - const { closePrompt, size, status, Prompt: PromptInner } = usePrompt(); + const { + size, + status, + closePrompt, + Prompt: PromptInner, + closeOnOutsideClick, + } = usePrompt(); if (status === 0) { return null; @@ -17,7 +23,15 @@ export const Prompt = () => { {PromptInner} - diff --git a/packages/app/src/library/Signers/VaultSigner.ts b/packages/app/src/library/Signers/VaultSigner/index.ts similarity index 79% rename from packages/app/src/library/Signers/VaultSigner.ts rename to packages/app/src/library/Signers/VaultSigner/index.ts index 96998dce5..9ae389d12 100644 --- a/packages/app/src/library/Signers/VaultSigner.ts +++ b/packages/app/src/library/Signers/VaultSigner/index.ts @@ -6,38 +6,34 @@ import type { V15 } from '@polkadot-api/substrate-bindings'; import { decAnyMetadata } from '@polkadot-api/substrate-bindings'; import type { PolkadotSigner } from 'polkadot-api'; import { mergeUint8 } from 'polkadot-api/utils'; -import type { AnyApi } from 'types'; +import type { VaultPromptHandlers, VaultSignStatus } from './types'; export class VaultSigner { #publicKey: Uint8Array; - #promptHandlers: { - openPrompt: ( - onComplete: (result: Uint8Array) => void, - toSign: Uint8Array - ) => void; - closePrompt: () => void; - }; + #promptHandlers: VaultPromptHandlers; - constructor( - pubKey: Uint8Array, - promptHandlers: { openPrompt: AnyApi; closePrompt: AnyApi } - ) { + constructor(pubKey: Uint8Array, promptHandlers: VaultPromptHandlers) { this.#publicKey = pubKey; this.#promptHandlers = promptHandlers; } #showPrompt = (toSign: Uint8Array) => new Promise((resolve) => { - const handleComplete = (result: Uint8Array) => { + const handleComplete = (status: VaultSignStatus, result: Uint8Array) => { this.#promptHandlers.closePrompt(); - resolve(result); + if (status === 'cancelled') { + this.#promptHandlers.setSubmitting(false); + resolve(null); + } else { + resolve(result); + } }; + // Show prompt, passing completion handler to call once user has completed signing. This will // resolve the promise and continue with signing. - this.#promptHandlers.openPrompt( - (result: Uint8Array) => handleComplete(result), - toSign - ); + this.#promptHandlers.openPrompt((status, signature) => { + handleComplete(status, signature); + }, toSign); }); async getPolkadotSigner(networkInfo: { @@ -65,7 +61,8 @@ export class VaultSigner { additionalSigned.push(signedExtension.additionalSigned); }); - const prefix = new Uint8Array([8]); + // The byte length is required as a prefix here. + const prefix = new Uint8Array([callData.length * 4]); const toSign = mergeUint8( prefix, callData, @@ -73,6 +70,8 @@ export class VaultSigner { ...additionalSigned ); + console.log(toSign); + // Start flow to sign QR Code here. const userResponse = await this.#showPrompt(toSign); diff --git a/packages/app/src/library/Signers/VaultSigner/types.ts b/packages/app/src/library/Signers/VaultSigner/types.ts new file mode 100644 index 000000000..608d04ce2 --- /dev/null +++ b/packages/app/src/library/Signers/VaultSigner/types.ts @@ -0,0 +1,13 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +export interface VaultPromptHandlers { + openPrompt: ( + onComplete: (status: 'complete' | 'cancelled', result: Uint8Array) => void, + toSign: Uint8Array + ) => void; + closePrompt: () => void; + setSubmitting: (submitting: boolean) => void; +} + +export type VaultSignStatus = 'complete' | 'cancelled'; diff --git a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx index 3f9d6de90..99aa0bd1a 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx @@ -25,7 +25,6 @@ export const SignPrompt = ({ const { chainSpecs: { genesisHash }, } = useApi(); - const { t } = useTranslation('library'); // Whether user is on sign or submit stage. @@ -61,8 +60,7 @@ export const SignPrompt = ({ { - // TODO: Expand with result `status` of cancelled or complete. - onComplete(signature); + onComplete('complete', signature); }} /> @@ -94,8 +92,7 @@ export const SignPrompt = ({ lg marginLeft onClick={() => { - // TODO: Expand with result `status` of cancelled or complete. - onComplete(new Uint8Array(0)); + onComplete('cancelled', new Uint8Array(0)); }} /> diff --git a/packages/app/src/library/SubmitTx/types.ts b/packages/app/src/library/SubmitTx/types.ts index f47868302..96663e3be 100644 --- a/packages/app/src/library/SubmitTx/types.ts +++ b/packages/app/src/library/SubmitTx/types.ts @@ -27,7 +27,7 @@ export interface SubmitProps { export interface SignerPromptProps { submitAddress: MaybeAddress; toSign: Uint8Array; - onComplete: (result: Uint8Array) => void; + onComplete: (status: 'complete' | 'cancelled', signature: Uint8Array) => void; } export interface LedgerSubmitProps { From 9c62543b9f29e24b84bc1c9827eef922f6b64ccc Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 11:50:06 +0700 Subject: [PATCH 69/84] vault signer working --- .../src/hooks/useSubmitExtrinsic/index.tsx | 12 +++-- .../src/library/Signers/VaultSigner/index.ts | 54 ++++++++++--------- .../src/library/Signers/VaultSigner/types.ts | 7 ++- .../SubmitTx/ManualSign/Vault/SignPrompt.tsx | 2 +- packages/app/src/library/SubmitTx/types.ts | 6 ++- 5 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 8e30bac78..3a9a869c2 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -30,7 +30,10 @@ import type { LedgerAccount } from '@w3ux/react-connect-kit/types'; import { VaultSigner } from 'library/Signers/VaultSigner'; import { usePrompt } from 'contexts/Prompt'; import { SignPrompt } from 'library/SubmitTx/ManualSign/Vault/SignPrompt'; -import type { VaultSignStatus } from 'library/Signers/VaultSigner/types'; +import type { + VaultSignatureResult, + VaultSignStatus, +} from 'library/Signers/VaultSigner/types'; export const useSubmitExtrinsic = ({ tx, @@ -249,7 +252,10 @@ export const useSubmitExtrinsic = ({ case 'vault': signer = await new VaultSigner(pubKey, { openPrompt: ( - onComplete: (status: VaultSignStatus, result: Uint8Array) => void, + onComplete: ( + status: VaultSignStatus, + result: VaultSignatureResult + ) => void, toSign: Uint8Array ) => { openPromptWith( @@ -264,7 +270,7 @@ export const useSubmitExtrinsic = ({ }, closePrompt: () => closePrompt(), setSubmitting, - }).getPolkadotSigner(networkInfo); + }).getPolkadotSigner(); break; case 'wallet_connect': diff --git a/packages/app/src/library/Signers/VaultSigner/index.ts b/packages/app/src/library/Signers/VaultSigner/index.ts index 9ae389d12..7a4173a44 100644 --- a/packages/app/src/library/Signers/VaultSigner/index.ts +++ b/packages/app/src/library/Signers/VaultSigner/index.ts @@ -3,10 +3,14 @@ import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; import type { V15 } from '@polkadot-api/substrate-bindings'; -import { decAnyMetadata } from '@polkadot-api/substrate-bindings'; +import { Binary, decAnyMetadata } from '@polkadot-api/substrate-bindings'; import type { PolkadotSigner } from 'polkadot-api'; import { mergeUint8 } from 'polkadot-api/utils'; -import type { VaultPromptHandlers, VaultSignStatus } from './types'; +import type { + VaultPromptHandlers, + VaultSignatureResult, + VaultSignStatus, +} from './types'; export class VaultSigner { #publicKey: Uint8Array; @@ -17,9 +21,12 @@ export class VaultSigner { this.#promptHandlers = promptHandlers; } - #showPrompt = (toSign: Uint8Array) => + #showPrompt = (toSign: Uint8Array): Promise => new Promise((resolve) => { - const handleComplete = (status: VaultSignStatus, result: Uint8Array) => { + const handleComplete = ( + status: VaultSignStatus, + result: VaultSignatureResult + ) => { this.#promptHandlers.closePrompt(); if (status === 'cancelled') { this.#promptHandlers.setSubmitting(false); @@ -28,28 +35,18 @@ export class VaultSigner { resolve(result); } }; - - // Show prompt, passing completion handler to call once user has completed signing. This will - // resolve the promise and continue with signing. this.#promptHandlers.openPrompt((status, signature) => { handleComplete(status, signature); }, toSign); }); - async getPolkadotSigner(networkInfo: { - decimals: number; - tokenSymbol: string; - }): Promise { + async getPolkadotSigner(): Promise { const signTx: PolkadotSigner['signTx'] = async ( callData, signedExtensions, metadata ) => { - console.debug(networkInfo); - // const merkleizer = merkleizeMetadata(metadata, networkInfo); - // const digest = merkleizer.digest(); const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; - const extra: Uint8Array[] = []; const additionalSigned: Uint8Array[] = []; v15.extrinsic.signedExtensions.map(({ identifier }) => { @@ -70,27 +67,32 @@ export class VaultSigner { ...additionalSigned ); - console.log(toSign); - // Start flow to sign QR Code here. - const userResponse = await this.#showPrompt(toSign); + const signature = await this.#showPrompt(toSign); - console.log('user response: ', userResponse); - // NOTE: Placeholder. - const signature = Buffer.from(new Uint8Array(0)); - return createV4Tx(v15, this.#publicKey, signature, extra, callData); + if (signature === null) { + throw 'Invalid signature'; + } + return createV4Tx( + v15, + this.#publicKey, + Binary.fromHex(signature).asBytes(), + extra, + callData + ); }; return { publicKey: this.#publicKey, signTx, signBytes: getSignBytes(async (x) => { - const signature = Buffer.from(x); - // TODO: Replace with vault signature. - // const { signature } = await Ledger.signPayload(app, index, x); + const signatureHex = await this.#showPrompt(x); + if (!signatureHex) { + throw 'Invalid signature'; + } // NOTE: the signature includes a "0x00" at the beginning, indicating a ed25519 signature. // this is not needed for non-extrinsic signatures. - return signature.subarray(1); + return Binary.fromHex(signatureHex).asBytes().subarray(1); }), }; } diff --git a/packages/app/src/library/Signers/VaultSigner/types.ts b/packages/app/src/library/Signers/VaultSigner/types.ts index 608d04ce2..e84578a96 100644 --- a/packages/app/src/library/Signers/VaultSigner/types.ts +++ b/packages/app/src/library/Signers/VaultSigner/types.ts @@ -3,7 +3,10 @@ export interface VaultPromptHandlers { openPrompt: ( - onComplete: (status: 'complete' | 'cancelled', result: Uint8Array) => void, + onComplete: ( + status: 'complete' | 'cancelled', + result: `0x${string}` | null + ) => void, toSign: Uint8Array ) => void; closePrompt: () => void; @@ -11,3 +14,5 @@ export interface VaultPromptHandlers { } export type VaultSignStatus = 'complete' | 'cancelled'; + +export type VaultSignatureResult = `0x${string}` | null; diff --git a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx index 99aa0bd1a..2591261f9 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/Vault/SignPrompt.tsx @@ -92,7 +92,7 @@ export const SignPrompt = ({ lg marginLeft onClick={() => { - onComplete('cancelled', new Uint8Array(0)); + onComplete('cancelled', null); }} /> diff --git a/packages/app/src/library/SubmitTx/types.ts b/packages/app/src/library/SubmitTx/types.ts index 96663e3be..4fdcd0f0d 100644 --- a/packages/app/src/library/SubmitTx/types.ts +++ b/packages/app/src/library/SubmitTx/types.ts @@ -3,6 +3,7 @@ import type { IconProp } from '@fortawesome/fontawesome-svg-core'; import type { DisplayFor } from '@w3ux/types'; +import type { VaultSignatureResult } from 'library/Signers/VaultSigner/types'; import type { ReactNode } from 'react'; import type { MaybeAddress } from 'types'; @@ -27,7 +28,10 @@ export interface SubmitProps { export interface SignerPromptProps { submitAddress: MaybeAddress; toSign: Uint8Array; - onComplete: (status: 'complete' | 'cancelled', signature: Uint8Array) => void; + onComplete: ( + status: 'complete' | 'cancelled', + signature: VaultSignatureResult + ) => void; } export interface LedgerSubmitProps { From f768b6c1a27169c9d5cab146b89ce955175eb875 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 14:32:05 +0700 Subject: [PATCH 70/84] simplify wc sign button --- .../ManualSign/WalletConnect/index.tsx | 72 ++++++------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx b/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx index b1dbdc57d..24dff2de1 100644 --- a/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx +++ b/packages/app/src/library/SubmitTx/ManualSign/WalletConnect/index.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import { faSquarePen } from '@fortawesome/free-solid-svg-icons'; -import { useState, type ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { useTxMeta } from 'contexts/TxMeta'; import { EstimatedTxFee } from 'library/EstimatedTxFee'; @@ -12,7 +12,6 @@ import { ButtonSubmit } from 'ui-buttons'; import { ButtonSubmitLarge } from 'library/SubmitTx/ButtonSubmitLarge'; import { appendOrEmpty } from '@w3ux/utils'; import { useWalletConnect } from 'contexts/WalletConnect'; -import { useApi } from 'contexts/Api'; export const WalletConnect = ({ onSubmit, @@ -24,70 +23,43 @@ export const WalletConnect = ({ displayFor, }: SubmitProps & { buttons?: ReactNode[] }) => { const { t } = useTranslation('library'); - const { - chainSpecs: { genesisHash }, - } = useApi(); + const { txFeesValid, sender } = useTxMeta(); const { accountHasSigner } = useImportedAccounts(); - const { wcSessionActive, connectProvider, fetchAddresses, signWcTx } = + const { wcSessionActive, connectProvider, fetchAddresses } = useWalletConnect(); - const { txFeesValid, sender, getTxPayloadJson, setTxSignature } = useTxMeta(); - - // Store whether the user is currently signing a transaction. - const [isSgning, setIsSigning] = useState(false); // The state under which submission is disabled. - const disabled = - submitting || !valid || !accountHasSigner(submitAddress) || !txFeesValid; - const alreadySubmitted = submitting; + const disabled = !valid || !accountHasSigner(submitAddress) || !txFeesValid; // Format submit button based on whether signature currently exists or submission is ongoing. let buttonOnClick: () => void; let buttonDisabled: boolean; let buttonPulse: boolean; - if (alreadySubmitted) { - buttonOnClick = onSubmit; + const connectAndSubmit = async () => { + // If Wallet Connect session is not active, re-connect. + if (!wcSessionActive) { + await connectProvider(); + } + const wcAccounts = await fetchAddresses(); + const accountExists = sender && wcAccounts.includes(sender); + if (!sender || !accountExists) { + return; + } + onSubmit(); + }; + + if (submitting) { + buttonOnClick = connectAndSubmit; buttonDisabled = disabled; - buttonPulse = valid; + buttonPulse = false; } else { - buttonOnClick = async () => { - // If Wallet Connect session is not active, re-connect. - if (!wcSessionActive) { - await connectProvider(); - } - - const wcAccounts = await fetchAddresses(); - const accountExists = sender && wcAccounts.includes(sender); - - const payload = getTxPayloadJson(); - if (!sender || !payload || !accountExists) { - return; - } - - setIsSigning(true); - - const caip = `polkadot:${genesisHash.substring(2).substring(0, 32)}`; - - try { - const signature = await signWcTx(caip, payload, sender); - if (signature) { - setTxSignature(signature); - } - } catch (e) { - setIsSigning(false); - } - setIsSigning(false); - }; - + buttonOnClick = connectAndSubmit; buttonDisabled = disabled; buttonPulse = !disabled; } - const buttonText = alreadySubmitted - ? submitText || '' - : isSgning - ? t('signing') - : t('sign'); + const buttonText = submitting ? submitText || '' : t('sign'); return (
From f59cffc5925a9105a256937ce1d1f365d91a6611 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 14:32:16 +0700 Subject: [PATCH 71/84] update types --- packages/app/src/contexts/WalletConnect/types.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/app/src/contexts/WalletConnect/types.ts b/packages/app/src/contexts/WalletConnect/types.ts index 99499995c..4fa1b6c5e 100644 --- a/packages/app/src/contexts/WalletConnect/types.ts +++ b/packages/app/src/contexts/WalletConnect/types.ts @@ -11,14 +11,16 @@ export interface WalletConnectContextInterface { updateWcSession: () => Promise; disconnectWcSession: () => Promise; fetchAddresses: () => Promise; - signWcTx: ( - caip: string, - payload: AnyJson, - from: string - ) => Promise; + signWcTx: WalletConnectSignTx; } export interface WalletConnectConnectedMeta { uri: string | undefined; approval: AnyFunction; } + +export type WalletConnectSignTx = ( + caip: string, + payload: AnyJson, + from: string +) => Promise; From a2c3ffabe00f7292ed8160abe476a1e5c38dac7d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 14:38:19 +0700 Subject: [PATCH 72/84] almost working --- .../src/hooks/useSubmitExtrinsic/index.tsx | 24 +++- .../Signers/WallectConnectSigner/index.ts | 135 ++++++++++++++++++ 2 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/library/Signers/WallectConnectSigner/index.ts diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 3a9a869c2..53e632b16 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -34,6 +34,9 @@ import type { VaultSignatureResult, VaultSignStatus, } from 'library/Signers/VaultSigner/types'; +import { WallectConnectSigner } from 'library/Signers/WallectConnectSigner'; +import { useApi } from 'contexts/Api'; +import { useWalletConnect } from 'contexts/WalletConnect'; export const useSubmitExtrinsic = ({ tx, @@ -47,7 +50,9 @@ export const useSubmitExtrinsic = ({ network, networkData: { units, unit }, } = useNetwork(); + const { chainSpecs } = useApi(); const { getNonce } = useBalances(); + const { signWcTx } = useWalletConnect(); const { activeProxy } = useActiveAccounts(); const { extensionsStatus } = useExtensions(); const { isProxySupported } = useProxySupported(); @@ -136,6 +141,8 @@ export const useSubmitExtrinsic = ({ // Extrinsic submission handler. const onSubmit = async () => { + const { pApi } = ApiController.get(network); + const account = getAccount(fromRef.current); if (account === null || submitting || !shouldSubmit) { return; @@ -240,6 +247,10 @@ export const useSubmitExtrinsic = ({ decimals: units, tokenSymbol: unit, }; + const blockNumber = await pApi.query.System.Number.getValue(); + const blockHash = await pApi.query.System.BlockHash.getValue( + blockNumber - 1 + ); switch (source) { case 'ledger': @@ -274,7 +285,16 @@ export const useSubmitExtrinsic = ({ break; case 'wallet_connect': - // TODO: Implement + signer = await new WallectConnectSigner( + pubKey, + `polkadot:${chainSpecs.genesisHash.substring(2).substring(0, 32)}`, + signWcTx, + chainSpecs, + Number(nonce), + fromRef.current, + blockNumber, + blockHash.asHex() + ).getPolkadotSigner(); break; } } else { @@ -302,6 +322,7 @@ export const useSubmitExtrinsic = ({ } }, error: (err: Error) => { + console.log(err); if (err instanceof InvalidTxError) { onFailedTx(); } @@ -309,6 +330,7 @@ export const useSubmitExtrinsic = ({ }, }); } catch (e) { + console.log(e); onError('default'); } }; diff --git a/packages/app/src/library/Signers/WallectConnectSigner/index.ts b/packages/app/src/library/Signers/WallectConnectSigner/index.ts new file mode 100644 index 000000000..171562998 --- /dev/null +++ b/packages/app/src/library/Signers/WallectConnectSigner/index.ts @@ -0,0 +1,135 @@ +// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors +// SPDX-License-Identifier: GPL-3.0-only + +import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; +import type { V15 } from '@polkadot-api/substrate-bindings'; +import { + Binary, + compact, + decAnyMetadata, + u32, +} from '@polkadot-api/substrate-bindings'; +import type { WalletConnectSignTx } from 'contexts/WalletConnect/types'; +import type { PapiChainSpec } from 'model/Api/types'; +import type { PolkadotSigner } from 'polkadot-api'; +import { toHex } from 'polkadot-api/utils'; + +export class WallectConnectSigner { + #publicKey: Uint8Array; + #who: string; + #caip: string; + #signFn: WalletConnectSignTx; + #chainSpecs: PapiChainSpec; + #nonce: number; + #blockNumber: number; + #blockHash: string; + + constructor( + pubKey: Uint8Array, + caip: string, + signFn: WalletConnectSignTx, + chainSpecs: PapiChainSpec, + nonce: number, + who: string, + blockNumber: number, + blockHash: string + ) { + this.#publicKey = pubKey; + this.#caip = caip; + this.#signFn = signFn; + this.#chainSpecs = chainSpecs; + this.#who = who; + this.#nonce = nonce; + this.#blockNumber = blockNumber; + this.#blockHash = blockHash; + } + + #toPjsHex(value: number | bigint, minByteLen?: number) { + let inner = value.toString(16); + inner = (inner.length % 2 ? '0' : '') + inner; + const nPaddedBytes = Math.max(0, (minByteLen || 0) - inner.length / 2); + return '0x' + '00'.repeat(nPaddedBytes) + inner; + } + + async getPolkadotSigner(): Promise { + const signTx: PolkadotSigner['signTx'] = async ( + callData, + signedExtensions, + metadata + ) => { + const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; + const identifiers: string[] = []; + const extra: Uint8Array[] = []; + const additionalSigned: Uint8Array[] = []; + v15.extrinsic.signedExtensions.map(({ identifier }) => { + const signedExtension = signedExtensions[identifier]; + if (!signedExtension) { + throw new Error(`Missing ${identifier} signed extension`); + } + identifiers.push(identifier); + extra.push(signedExtension.value); + additionalSigned.push(signedExtension.additionalSigned); + }); + + const { version } = v15.extrinsic; + const lastBlock = this.#blockNumber - 1; + // const phase = lastBlock % 64; + // const period = 6; + // const era = toHex(new Uint8Array([phase, period])); + + const unsignedTransaction = { + specVersion: this.#toPjsHex( + u32.dec(u32.enc(this.#chainSpecs.specVersion)), + 4 + ), + transactionVersion: this.#toPjsHex( + u32.dec(u32.enc(this.#chainSpecs.transactionVersion)) + ), + version, + address: this.#who, + blockHash: this.#blockHash, + blockNumber: this.#toPjsHex(u32.dec(u32.enc(lastBlock)), 4), + era: '0x00', + genesisHash: this.#chainSpecs.genesisHash, + method: toHex(callData), + nonce: this.#toPjsHex(compact.dec(compact.enc(this.#nonce)), 4), + signedExtensions: identifiers, + tip: this.#toPjsHex(u32.dec(u32.enc(0)), 16), + }; + + // Await signature from Wallet Connect. + const signature = await this.#signFn( + this.#caip, + unsignedTransaction, + this.#who + ); + + console.log(signature); + + if (signature === null) { + throw 'Invalid signature'; + } + return createV4Tx( + v15, + this.#publicKey, + Binary.fromHex(signature).asBytes(), + extra, + callData + ); + }; + + return { + publicKey: this.#publicKey, + signTx, + signBytes: getSignBytes(async (x) => { + const signatureHex = await this.#signFn(this.#caip, x, this.#who); + if (!signatureHex) { + throw 'Invalid signature'; + } + // NOTE: the signature includes a "0x00" at the beginning, indicating a ed25519 signature. + // this is not needed for non-extrinsic signatures. + return Binary.fromHex(signatureHex).asBytes().subarray(1); + }), + }; + } +} From bbc28531c4f227a913a40c0437564378ae049b36 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 15:11:21 +0700 Subject: [PATCH 73/84] fix block hash & number --- .../app/src/contexts/WalletConnect/index.tsx | 23 ++++++++++--------- .../src/hooks/useSubmitExtrinsic/index.tsx | 11 ++++----- .../Signers/WallectConnectSigner/index.ts | 14 +++++------ 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/packages/app/src/contexts/WalletConnect/index.tsx b/packages/app/src/contexts/WalletConnect/index.tsx index c67666d82..eb79f22f8 100644 --- a/packages/app/src/contexts/WalletConnect/index.tsx +++ b/packages/app/src/contexts/WalletConnect/index.tsx @@ -261,18 +261,19 @@ export const WalletConnectProvider = ({ } const topic = wcProvider.current.session.topic; - const result: { signature: string } = - await wcProvider.current.client.request({ - chainId: caip, - topic, - request: { - method: 'polkadot_signTransaction', - params: { - address: from, - transactionPayload: payload, - }, + const request = { + chainId: caip, + topic, + request: { + method: 'polkadot_signTransaction', + params: { + address: from, + transactionPayload: payload, }, - }); + }, + }; + const result: { signature: string } = + await wcProvider.current.client.request(request); return result?.signature || null; }; diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 53e632b16..594909838 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -141,7 +141,7 @@ export const useSubmitExtrinsic = ({ // Extrinsic submission handler. const onSubmit = async () => { - const { pApi } = ApiController.get(network); + const { papiClient } = ApiController.get(network); const account = getAccount(fromRef.current); if (account === null || submitting || !shouldSubmit) { @@ -247,10 +247,7 @@ export const useSubmitExtrinsic = ({ decimals: units, tokenSymbol: unit, }; - const blockNumber = await pApi.query.System.Number.getValue(); - const blockHash = await pApi.query.System.BlockHash.getValue( - blockNumber - 1 - ); + const { number, hash } = await papiClient.getFinalizedBlock(); switch (source) { case 'ledger': @@ -292,8 +289,8 @@ export const useSubmitExtrinsic = ({ chainSpecs, Number(nonce), fromRef.current, - blockNumber, - blockHash.asHex() + number, + hash ).getPolkadotSigner(); break; } diff --git a/packages/app/src/library/Signers/WallectConnectSigner/index.ts b/packages/app/src/library/Signers/WallectConnectSigner/index.ts index 171562998..3b16ab2c6 100644 --- a/packages/app/src/library/Signers/WallectConnectSigner/index.ts +++ b/packages/app/src/library/Signers/WallectConnectSigner/index.ts @@ -72,10 +72,9 @@ export class WallectConnectSigner { }); const { version } = v15.extrinsic; - const lastBlock = this.#blockNumber - 1; - // const phase = lastBlock % 64; - // const period = 6; - // const era = toHex(new Uint8Array([phase, period])); + const phase = this.#blockNumber % 64; + const period = 6; + const era = toHex(new Uint8Array([phase, period])); const unsignedTransaction = { specVersion: this.#toPjsHex( @@ -83,14 +82,15 @@ export class WallectConnectSigner { 4 ), transactionVersion: this.#toPjsHex( - u32.dec(u32.enc(this.#chainSpecs.transactionVersion)) + u32.dec(u32.enc(this.#chainSpecs.transactionVersion)), + 4 ), version, address: this.#who, blockHash: this.#blockHash, - blockNumber: this.#toPjsHex(u32.dec(u32.enc(lastBlock)), 4), - era: '0x00', + blockNumber: this.#toPjsHex(u32.dec(u32.enc(this.#blockNumber)), 4), genesisHash: this.#chainSpecs.genesisHash, + era, method: toHex(callData), nonce: this.#toPjsHex(compact.dec(compact.enc(this.#nonce)), 4), signedExtensions: identifiers, From 2c6f3bb2aa639c1cc6cd92bd8c8fd6b454a41cca Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 15:45:25 +0700 Subject: [PATCH 74/84] some tidy up --- .../Signers/WallectConnectSigner/index.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/app/src/library/Signers/WallectConnectSigner/index.ts b/packages/app/src/library/Signers/WallectConnectSigner/index.ts index 3b16ab2c6..a9e67fb8d 100644 --- a/packages/app/src/library/Signers/WallectConnectSigner/index.ts +++ b/packages/app/src/library/Signers/WallectConnectSigner/index.ts @@ -59,6 +59,15 @@ export class WallectConnectSigner { ) => { const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; const identifiers: string[] = []; + + v15.extrinsic.signedExtensions.map(({ identifier }) => { + const signedExtension = signedExtensions[identifier]; + if (!signedExtension) { + throw new Error(`Missing ${identifier} signed extension`); + } + identifiers.push(identifier); + }); + const extra: Uint8Array[] = []; const additionalSigned: Uint8Array[] = []; v15.extrinsic.signedExtensions.map(({ identifier }) => { @@ -72,10 +81,6 @@ export class WallectConnectSigner { }); const { version } = v15.extrinsic; - const phase = this.#blockNumber % 64; - const period = 6; - const era = toHex(new Uint8Array([phase, period])); - const unsignedTransaction = { specVersion: this.#toPjsHex( u32.dec(u32.enc(this.#chainSpecs.specVersion)), @@ -85,16 +90,16 @@ export class WallectConnectSigner { u32.dec(u32.enc(this.#chainSpecs.transactionVersion)), 4 ), - version, address: this.#who, blockHash: this.#blockHash, blockNumber: this.#toPjsHex(u32.dec(u32.enc(this.#blockNumber)), 4), genesisHash: this.#chainSpecs.genesisHash, - era, + era: '0x00', // Immortal until working. method: toHex(callData), nonce: this.#toPjsHex(compact.dec(compact.enc(this.#nonce)), 4), signedExtensions: identifiers, tip: this.#toPjsHex(u32.dec(u32.enc(0)), 16), + version, }; // Await signature from Wallet Connect. @@ -104,8 +109,6 @@ export class WallectConnectSigner { this.#who ); - console.log(signature); - if (signature === null) { throw 'Invalid signature'; } From 40d70046cce772a7555b978bccfa4c4849a8dce3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 15:46:52 +0700 Subject: [PATCH 75/84] mv file --- packages/app/src/hooks/useSubmitExtrinsic/index.tsx | 2 +- .../{WallectConnectSigner/index.ts => WalletConnectSigner.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/app/src/library/Signers/{WallectConnectSigner/index.ts => WalletConnectSigner.ts} (100%) diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 594909838..bd8476cc4 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -34,7 +34,7 @@ import type { VaultSignatureResult, VaultSignStatus, } from 'library/Signers/VaultSigner/types'; -import { WallectConnectSigner } from 'library/Signers/WallectConnectSigner'; +import { WallectConnectSigner } from 'library/Signers/WalletConnectSigner'; import { useApi } from 'contexts/Api'; import { useWalletConnect } from 'contexts/WalletConnect'; diff --git a/packages/app/src/library/Signers/WallectConnectSigner/index.ts b/packages/app/src/library/Signers/WalletConnectSigner.ts similarity index 100% rename from packages/app/src/library/Signers/WallectConnectSigner/index.ts rename to packages/app/src/library/Signers/WalletConnectSigner.ts From 7a5369b41d5e37a9f3e4d9dff8f7a936e0a1b80d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 15:54:18 +0700 Subject: [PATCH 76/84] amendments --- .../app/src/library/Signers/WalletConnectSigner.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/app/src/library/Signers/WalletConnectSigner.ts b/packages/app/src/library/Signers/WalletConnectSigner.ts index a9e67fb8d..78b17e423 100644 --- a/packages/app/src/library/Signers/WalletConnectSigner.ts +++ b/packages/app/src/library/Signers/WalletConnectSigner.ts @@ -60,14 +60,6 @@ export class WallectConnectSigner { const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; const identifiers: string[] = []; - v15.extrinsic.signedExtensions.map(({ identifier }) => { - const signedExtension = signedExtensions[identifier]; - if (!signedExtension) { - throw new Error(`Missing ${identifier} signed extension`); - } - identifiers.push(identifier); - }); - const extra: Uint8Array[] = []; const additionalSigned: Uint8Array[] = []; v15.extrinsic.signedExtensions.map(({ identifier }) => { @@ -81,6 +73,7 @@ export class WallectConnectSigner { }); const { version } = v15.extrinsic; + const unsignedTransaction = { specVersion: this.#toPjsHex( u32.dec(u32.enc(this.#chainSpecs.specVersion)), @@ -90,6 +83,7 @@ export class WallectConnectSigner { u32.dec(u32.enc(this.#chainSpecs.transactionVersion)), 4 ), + version, address: this.#who, blockHash: this.#blockHash, blockNumber: this.#toPjsHex(u32.dec(u32.enc(this.#blockNumber)), 4), @@ -99,7 +93,6 @@ export class WallectConnectSigner { nonce: this.#toPjsHex(compact.dec(compact.enc(this.#nonce)), 4), signedExtensions: identifiers, tip: this.#toPjsHex(u32.dec(u32.enc(0)), 16), - version, }; // Await signature from Wallet Connect. From 7c410a6c67cf978ec7420445d3a9158ab8c2d47c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 16:10:46 +0700 Subject: [PATCH 77/84] use fromHex --- packages/app/src/library/Signers/WalletConnectSigner.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/app/src/library/Signers/WalletConnectSigner.ts b/packages/app/src/library/Signers/WalletConnectSigner.ts index 78b17e423..24454197c 100644 --- a/packages/app/src/library/Signers/WalletConnectSigner.ts +++ b/packages/app/src/library/Signers/WalletConnectSigner.ts @@ -12,7 +12,7 @@ import { import type { WalletConnectSignTx } from 'contexts/WalletConnect/types'; import type { PapiChainSpec } from 'model/Api/types'; import type { PolkadotSigner } from 'polkadot-api'; -import { toHex } from 'polkadot-api/utils'; +import { fromHex, toHex } from 'polkadot-api/utils'; export class WallectConnectSigner { #publicKey: Uint8Array; @@ -105,10 +105,11 @@ export class WallectConnectSigner { if (signature === null) { throw 'Invalid signature'; } + return createV4Tx( v15, this.#publicKey, - Binary.fromHex(signature).asBytes(), + fromHex(signature), extra, callData ); From 2d25a6a1d0bda6fdde5c0b371b0a840be50a29be Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 20:04:10 +0700 Subject: [PATCH 78/84] use `getPolkadotSignerFromPjs` for WC --- .../src/contexts/WalletConnect/defaults.ts | 2 +- .../app/src/contexts/WalletConnect/index.tsx | 14 +- .../app/src/contexts/WalletConnect/types.ts | 6 +- .../src/hooks/useSubmitExtrinsic/index.tsx | 28 ++-- .../library/Signers/WalletConnectSigner.ts | 132 ------------------ 5 files changed, 20 insertions(+), 162 deletions(-) delete mode 100644 packages/app/src/library/Signers/WalletConnectSigner.ts diff --git a/packages/app/src/contexts/WalletConnect/defaults.ts b/packages/app/src/contexts/WalletConnect/defaults.ts index b3f6d5cf0..127662250 100644 --- a/packages/app/src/contexts/WalletConnect/defaults.ts +++ b/packages/app/src/contexts/WalletConnect/defaults.ts @@ -12,5 +12,5 @@ export const defaultWalletConnect: WalletConnectContextInterface = { disconnectWcSession: () => Promise.resolve(), wcSessionActive: false, fetchAddresses: () => Promise.resolve([]), - signWcTx: (caip, payload, from) => Promise.resolve(null), + signWcTx: (payload) => Promise.resolve({ signature: '0x' }), }; diff --git a/packages/app/src/contexts/WalletConnect/index.tsx b/packages/app/src/contexts/WalletConnect/index.tsx index eb79f22f8..b19a4adea 100644 --- a/packages/app/src/contexts/WalletConnect/index.tsx +++ b/packages/app/src/contexts/WalletConnect/index.tsx @@ -251,23 +251,19 @@ export const WalletConnectProvider = ({ }; // Attempt to sign a transaction and receive a signature. - const signWcTx = async ( - caip: string, - payload: AnyJson, - from: string - ): Promise => { + const signWcTx = async (payload: AnyJson): Promise<{ signature: string }> => { if (!wcProvider.current || !wcProvider.current.session?.topic) { - return null; + return { signature: '0x' }; } const topic = wcProvider.current.session.topic; - + const caip = `polkadot:${genesisHash.substring(2).substring(0, 32)}`; const request = { chainId: caip, topic, request: { method: 'polkadot_signTransaction', params: { - address: from, + address: payload.address, transactionPayload: payload, }, }, @@ -275,7 +271,7 @@ export const WalletConnectProvider = ({ const result: { signature: string } = await wcProvider.current.client.request(request); - return result?.signature || null; + return { signature: result?.signature || '0x' }; }; const fetchAddresses = async (): Promise => { diff --git a/packages/app/src/contexts/WalletConnect/types.ts b/packages/app/src/contexts/WalletConnect/types.ts index 4fa1b6c5e..77beb835c 100644 --- a/packages/app/src/contexts/WalletConnect/types.ts +++ b/packages/app/src/contexts/WalletConnect/types.ts @@ -20,7 +20,5 @@ export interface WalletConnectConnectedMeta { } export type WalletConnectSignTx = ( - caip: string, - payload: AnyJson, - from: string -) => Promise; + payload: AnyJson +) => Promise<{ signature: string }>; diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index bd8476cc4..159ef8e69 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -22,7 +22,10 @@ import { useNetwork } from 'contexts/Network'; import { useBalances } from 'contexts/Balances'; import type { PolkadotSigner } from 'polkadot-api'; import { AccountId, InvalidTxError } from 'polkadot-api'; -import { connectInjectedExtension } from 'polkadot-api/pjs-signer'; +import { + connectInjectedExtension, + getPolkadotSignerFromPjs, +} from 'polkadot-api/pjs-signer'; import { formatAccountSs58 } from '@w3ux/utils'; import { LedgerSigner } from 'library/Signers/LedgerSigner'; import { getLedgerApp } from 'contexts/LedgerHardware/Utils'; @@ -34,8 +37,6 @@ import type { VaultSignatureResult, VaultSignStatus, } from 'library/Signers/VaultSigner/types'; -import { WallectConnectSigner } from 'library/Signers/WalletConnectSigner'; -import { useApi } from 'contexts/Api'; import { useWalletConnect } from 'contexts/WalletConnect'; export const useSubmitExtrinsic = ({ @@ -50,7 +51,6 @@ export const useSubmitExtrinsic = ({ network, networkData: { units, unit }, } = useNetwork(); - const { chainSpecs } = useApi(); const { getNonce } = useBalances(); const { signWcTx } = useWalletConnect(); const { activeProxy } = useActiveAccounts(); @@ -141,8 +141,6 @@ export const useSubmitExtrinsic = ({ // Extrinsic submission handler. const onSubmit = async () => { - const { papiClient } = ApiController.get(network); - const account = getAccount(fromRef.current); if (account === null || submitting || !shouldSubmit) { return; @@ -247,7 +245,6 @@ export const useSubmitExtrinsic = ({ decimals: units, tokenSymbol: unit, }; - const { number, hash } = await papiClient.getFinalizedBlock(); switch (source) { case 'ledger': @@ -282,16 +279,15 @@ export const useSubmitExtrinsic = ({ break; case 'wallet_connect': - signer = await new WallectConnectSigner( - pubKey, - `polkadot:${chainSpecs.genesisHash.substring(2).substring(0, 32)}`, - signWcTx, - chainSpecs, - Number(nonce), + signer = getPolkadotSignerFromPjs( fromRef.current, - number, - hash - ).getPolkadotSigner(); + signWcTx, + // Signing bytes not currently being used. + async () => ({ + id: 0, + signature: '0x', + }) + ); break; } } else { diff --git a/packages/app/src/library/Signers/WalletConnectSigner.ts b/packages/app/src/library/Signers/WalletConnectSigner.ts deleted file mode 100644 index 24454197c..000000000 --- a/packages/app/src/library/Signers/WalletConnectSigner.ts +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors -// SPDX-License-Identifier: GPL-3.0-only - -import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; -import type { V15 } from '@polkadot-api/substrate-bindings'; -import { - Binary, - compact, - decAnyMetadata, - u32, -} from '@polkadot-api/substrate-bindings'; -import type { WalletConnectSignTx } from 'contexts/WalletConnect/types'; -import type { PapiChainSpec } from 'model/Api/types'; -import type { PolkadotSigner } from 'polkadot-api'; -import { fromHex, toHex } from 'polkadot-api/utils'; - -export class WallectConnectSigner { - #publicKey: Uint8Array; - #who: string; - #caip: string; - #signFn: WalletConnectSignTx; - #chainSpecs: PapiChainSpec; - #nonce: number; - #blockNumber: number; - #blockHash: string; - - constructor( - pubKey: Uint8Array, - caip: string, - signFn: WalletConnectSignTx, - chainSpecs: PapiChainSpec, - nonce: number, - who: string, - blockNumber: number, - blockHash: string - ) { - this.#publicKey = pubKey; - this.#caip = caip; - this.#signFn = signFn; - this.#chainSpecs = chainSpecs; - this.#who = who; - this.#nonce = nonce; - this.#blockNumber = blockNumber; - this.#blockHash = blockHash; - } - - #toPjsHex(value: number | bigint, minByteLen?: number) { - let inner = value.toString(16); - inner = (inner.length % 2 ? '0' : '') + inner; - const nPaddedBytes = Math.max(0, (minByteLen || 0) - inner.length / 2); - return '0x' + '00'.repeat(nPaddedBytes) + inner; - } - - async getPolkadotSigner(): Promise { - const signTx: PolkadotSigner['signTx'] = async ( - callData, - signedExtensions, - metadata - ) => { - const v15 = decAnyMetadata(metadata).metadata.value as unknown as V15; - const identifiers: string[] = []; - - const extra: Uint8Array[] = []; - const additionalSigned: Uint8Array[] = []; - v15.extrinsic.signedExtensions.map(({ identifier }) => { - const signedExtension = signedExtensions[identifier]; - if (!signedExtension) { - throw new Error(`Missing ${identifier} signed extension`); - } - identifiers.push(identifier); - extra.push(signedExtension.value); - additionalSigned.push(signedExtension.additionalSigned); - }); - - const { version } = v15.extrinsic; - - const unsignedTransaction = { - specVersion: this.#toPjsHex( - u32.dec(u32.enc(this.#chainSpecs.specVersion)), - 4 - ), - transactionVersion: this.#toPjsHex( - u32.dec(u32.enc(this.#chainSpecs.transactionVersion)), - 4 - ), - version, - address: this.#who, - blockHash: this.#blockHash, - blockNumber: this.#toPjsHex(u32.dec(u32.enc(this.#blockNumber)), 4), - genesisHash: this.#chainSpecs.genesisHash, - era: '0x00', // Immortal until working. - method: toHex(callData), - nonce: this.#toPjsHex(compact.dec(compact.enc(this.#nonce)), 4), - signedExtensions: identifiers, - tip: this.#toPjsHex(u32.dec(u32.enc(0)), 16), - }; - - // Await signature from Wallet Connect. - const signature = await this.#signFn( - this.#caip, - unsignedTransaction, - this.#who - ); - - if (signature === null) { - throw 'Invalid signature'; - } - - return createV4Tx( - v15, - this.#publicKey, - fromHex(signature), - extra, - callData - ); - }; - - return { - publicKey: this.#publicKey, - signTx, - signBytes: getSignBytes(async (x) => { - const signatureHex = await this.#signFn(this.#caip, x, this.#who); - if (!signatureHex) { - throw 'Invalid signature'; - } - // NOTE: the signature includes a "0x00" at the beginning, indicating a ed25519 signature. - // this is not needed for non-extrinsic signatures. - return Binary.fromHex(signatureHex).asBytes().subarray(1); - }), - }; - } -} From 74cb8085b6ddd2a5e92376804e87b9e7579ad683 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 20:58:37 +0700 Subject: [PATCH 79/84] pApi fixes --- .../src/canvas/JoinPool/Overview/JoinForm.tsx | 7 ++-- .../src/canvas/ManageNominations/index.tsx | 19 ++++------- .../canvas/NominatorSetup/Summary/index.tsx | 24 ++++++++----- packages/app/src/contexts/Payouts/index.tsx | 20 ++++++----- packages/app/src/contexts/Staking/index.tsx | 4 +-- .../Validators/ValidatorEntries/index.tsx | 6 ++-- packages/app/src/hooks/useBatchCall/index.tsx | 2 +- .../src/hooks/useSubmitExtrinsic/index.tsx | 4 +-- .../app/src/hooks/useSubmitExtrinsic/types.ts | 3 +- packages/app/src/modals/BalanceTest/index.tsx | 4 +-- packages/app/src/modals/Bond/index.tsx | 2 +- .../app/src/modals/ChangePoolRoles/index.tsx | 25 ++++++-------- packages/app/src/modals/ClaimReward/index.tsx | 4 ++- .../ManagePool/Forms/RenamePool/index.tsx | 2 +- .../Forms/SetClaimPermission/index.tsx | 3 +- .../ManagePool/Forms/SetPoolState/index.tsx | 6 ++-- .../app/src/modals/UnlockChunks/Forms.tsx | 16 +++++---- packages/app/src/modals/UpdatePayee/index.tsx | 25 ++++++++------ packages/app/src/model/Api/index.ts | 3 +- packages/app/src/model/Api/types.ts | 5 +-- .../app/src/model/ErasStakersPaged/index.tsx | 2 +- .../src/model/Query/ClaimedRewards/index.ts | 6 ++-- packages/app/src/model/Query/Era/index.ts | 34 ++++++++++--------- .../src/model/Query/ErasRewardPoints/index.ts | 4 +-- .../Query/ErasRewardPointsMulti/index.ts | 4 +-- .../model/Query/ErasStakersOverview/index.tsx | 2 +- .../model/Query/ErasValidatorReward/index.ts | 5 ++- .../Query/ErasValidatorRewardMulti/index.ts | 5 ++- .../app/src/model/Query/NetworkMeta/index.ts | 10 +++--- .../model/Query/ParaSessionAccounts/index.ts | 10 +++--- .../src/model/Query/ValidatorPrefs/index.ts | 6 ++-- .../src/model/Subscribe/ActiveEra/index.ts | 4 +-- .../Subscribe/ActivePoolAccount/index.ts | 4 +-- .../app/src/model/Subscribe/Bonded/index.tsx | 2 +- .../Subscribe/FastUnstakeConfig/index.ts | 5 ++- .../model/Subscribe/FastUnstakeQueue/index.ts | 2 +- .../model/Subscribe/PoolMembersMulti/index.ts | 34 ++++++++++++------- .../src/model/Subscribe/PoolsConfig/index.ts | 5 +-- .../model/Subscribe/StakingMetrics/index.ts | 12 ++++--- 39 files changed, 184 insertions(+), 156 deletions(-) diff --git a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx index 51ac1f894..82c03d0cb 100644 --- a/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx +++ b/packages/app/src/canvas/JoinPool/Overview/JoinForm.tsx @@ -24,6 +24,7 @@ import { planckToUnitBn } from 'library/Utils'; import { ApiController } from 'controllers/Api'; import { useBatchCall } from 'hooks/useBatchCall'; import { useSubmitExtrinsic } from 'hooks/useSubmitExtrinsic'; +import type { AnyApi } from 'types'; export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { const { t } = useTranslation(); @@ -82,7 +83,7 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { !bondValid ? '0' : bond.bond, units ).toString(); - const txs = [ + const txs: AnyApi[] = [ pApi.tx.NominationPools.join({ amount: BigInt(bondToSubmit), pool_id: bondedPool.id, @@ -92,7 +93,9 @@ export const JoinForm = ({ bondedPool }: OverviewSectionProps) => { // If claim permission is not the default, add it to tx. if (claimPermission !== defaultClaimPermission) { txs.push( - pApi.tx.NominationPools.set_claim_permission({ type: claimPermission }) + pApi.tx.NominationPools.set_claim_permission({ + permission: { type: claimPermission, value: undefined }, + }) ); } diff --git a/packages/app/src/canvas/ManageNominations/index.tsx b/packages/app/src/canvas/ManageNominations/index.tsx index 105d08abe..def16fe8b 100644 --- a/packages/app/src/canvas/ManageNominations/index.tsx +++ b/packages/app/src/canvas/ManageNominations/index.tsx @@ -99,26 +99,21 @@ export const ManageNominations = () => { return tx; } - // Note: `targets` structure differs between staking and pools. - const targetsToSubmit = newNominations.nominations.map((nominee) => - isPool - ? nominee.address - : { - type: 'Id', - value: nominee.address, - } - ); - if (isPool) { if (activePool) { tx = pApi.tx.NominationPools.nominate({ pool_id: activePool.id, - validators: targetsToSubmit, + validators: newNominations.nominations.map( + (nominee) => nominee.address + ), }); } } else { tx = pApi.tx.Staking.nominate({ - targets: targetsToSubmit, + targets: newNominations.nominations.map((nominee) => ({ + type: 'Id', + value: nominee.address, + })), }); } return tx; diff --git a/packages/app/src/canvas/NominatorSetup/Summary/index.tsx b/packages/app/src/canvas/NominatorSetup/Summary/index.tsx index 8555e6de6..aaddd2e4c 100644 --- a/packages/app/src/canvas/NominatorSetup/Summary/index.tsx +++ b/packages/app/src/canvas/NominatorSetup/Summary/index.tsx @@ -45,18 +45,19 @@ export const Summary = ({ section }: SetupStepProps) => { return null; } - const targetsToSubmit = nominations.map( - ({ address }: { address: string }) => ({ - type: 'Id', - value: address, - }) - ); + if (payee.destination === 'Account' && !payee.account) { + return null; + } + + if (payee.destination !== 'Account' && !payee.destination) { + return null; + } const payeeToSubmit = payee.destination === 'Account' ? { - type: 'Account', - value: payee.account, + type: 'Account' as const, + value: payee.account as string, } : { type: payee.destination, @@ -69,7 +70,12 @@ export const Summary = ({ section }: SetupStepProps) => { value: BigInt(bondToSubmit), payee: payeeToSubmit, }), - pApi.tx.Staking.nominate({ targets: targetsToSubmit }), + pApi.tx.Staking.nominate({ + targets: nominations.map(({ address }: { address: string }) => ({ + type: 'Id', + value: address, + })), + }), ]; return newBatchCall(txs, activeAccount); }; diff --git a/packages/app/src/contexts/Payouts/index.tsx b/packages/app/src/contexts/Payouts/index.tsx index a61013bca..f928a438d 100644 --- a/packages/app/src/contexts/Payouts/index.tsx +++ b/packages/app/src/contexts/Payouts/index.tsx @@ -198,7 +198,7 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { const results = await Promise.all( unclaimedRewardsEntries.map(([era, v]) => - new ClaimedRewards(pApi, era, v).fetch() + new ClaimedRewards(pApi, Number(era), v).fetch() ) ); @@ -213,11 +213,13 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { : undefined; // Add to `unclaimedRewards` if payout page has not yet been claimed. - if (!pages.includes(exposedPage)) { - if (unclaimedRewards?.[validator]) { - unclaimedRewards[validator].push(era); - } else { - unclaimedRewards[validator] = [era]; + if (exposedPage) { + if (!pages.includes(exposedPage)) { + if (unclaimedRewards?.[validator]) { + unclaimedRewards[validator].push(era); + } else { + unclaimedRewards[validator] = [era]; + } } } } @@ -242,10 +244,10 @@ export const PayoutsProvider = ({ children }: { children: ReactNode }) => { if (validators.length > 0) { calls.push( Promise.all([ - new ErasValidatorReward(pApi, era).fetch(), - new ErasRewardPoints(pApi, era).fetch(), + new ErasValidatorReward(pApi, Number(era)).fetch(), + new ErasRewardPoints(pApi, Number(era)).fetch(), ...validators.map((validator: AnyJson) => - new ValidatorPrefs(pApi, era, validator).fetch() + new ValidatorPrefs(pApi, Number(era), validator).fetch() ), ]) ); diff --git a/packages/app/src/contexts/Staking/index.tsx b/packages/app/src/contexts/Staking/index.tsx index ea0cd3c49..8334dd1ad 100644 --- a/packages/app/src/contexts/Staking/index.tsx +++ b/packages/app/src/contexts/Staking/index.tsx @@ -229,7 +229,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { return []; } - const overview = await new ErasStakersOverview(pApi).fetch(era); + const overview = await new ErasStakersOverview(pApi).fetch(Number(era)); const validators: Record = overview.reduce( ( prev: Record, @@ -240,7 +240,7 @@ export const StakingProvider = ({ children }: { children: ReactNode }) => { const validatorKeys = Object.keys(validators); const pagedResults = await Promise.all( - validatorKeys.map((v) => new ErasStakersPaged(pApi).fetch(era, v)) + validatorKeys.map((v) => new ErasStakersPaged(pApi).fetch(Number(era), v)) ); const result: Exposure[] = []; diff --git a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx index e4fd84fde..fcf9a29c1 100644 --- a/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx +++ b/packages/app/src/contexts/Validators/ValidatorEntries/index.tsx @@ -181,7 +181,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { erasProcessed.isLessThan(totalEras) ); - const erasMulti: [string][] = eras.map((e) => [e.toString()]); + const erasMulti: [number][] = eras.map((e) => [e.toNumber()]); const results = await new ErasRewardPointsMulti(pApi, erasMulti).fetch(); // Make calls and format reward point results. @@ -354,7 +354,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { setSessionParaValidators( await new ParaSessionAccounts( pApi, - earliestStoredSession.toString() + earliestStoredSession.toNumber() ).fetch() ); }; @@ -513,7 +513,7 @@ export const ValidatorsProvider = ({ children }: { children: ReactNode }) => { thisEra = thisEra.minus(1); } while (thisEra.gte(endEra)); - const erasMulti: [string][] = eras.map((e) => [e.toString()]); + const erasMulti: [number][] = eras.map((e) => [Number(e)]); const results = await new ErasValidatorReward(pApi, erasMulti).fetch(); const reward = results diff --git a/packages/app/src/hooks/useBatchCall/index.tsx b/packages/app/src/hooks/useBatchCall/index.tsx index 1c882d47e..58b6efc9b 100644 --- a/packages/app/src/hooks/useBatchCall/index.tsx +++ b/packages/app/src/hooks/useBatchCall/index.tsx @@ -34,7 +34,7 @@ export const useBatchCall = () => { type: 'Id', value: from, }, - force_proxy_type: null, + force_proxy_type: undefined, call: tx.decodedCall, }).decodedCall ), diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 159ef8e69..55ea78bb1 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -114,9 +114,9 @@ export const useSubmitExtrinsic = ({ txRef.current = pApi.tx.Proxy.proxy({ real: { type: 'Id', - value: from, + value: from || '', }, - forceProxyType: null, + force_proxy_type: undefined, call: txRef.current.decodedCall, }); } diff --git a/packages/app/src/hooks/useSubmitExtrinsic/types.ts b/packages/app/src/hooks/useSubmitExtrinsic/types.ts index 292c83817..8284c34db 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/types.ts +++ b/packages/app/src/hooks/useSubmitExtrinsic/types.ts @@ -1,7 +1,6 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { UnsafeTransaction } from 'polkadot-api'; import type { AnyApi, MaybeAddress } from 'types'; export interface UseSubmitExtrinsicProps { @@ -21,4 +20,4 @@ export interface UseSubmitExtrinsic { } // eslint-disable-next-line @typescript-eslint/no-explicit-any -export type UnsafeTx = UnsafeTransaction; +export type UnsafeTx = AnyApi; diff --git a/packages/app/src/modals/BalanceTest/index.tsx b/packages/app/src/modals/BalanceTest/index.tsx index 7577ae2d3..ccf8c782d 100644 --- a/packages/app/src/modals/BalanceTest/index.tsx +++ b/packages/app/src/modals/BalanceTest/index.tsx @@ -39,14 +39,14 @@ export const BalanceTest = () => { type: 'Id', value: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', }, - value: unitToPlanck('0.1', units).toString(), + value: BigInt(unitToPlanck('0.1', units).toString()), }), pApi.tx.Balances.transfer_keep_alive({ dest: { type: 'Id', value: '1554u1a67ApEt5xmjbZwjgDNaVckbzB6cjRHWAQ1SpNkNxTd', }, - value: unitToPlanck('0.1', units).toString(), + value: BigInt(unitToPlanck('0.1', units).toString()), }), ]; const batch = newBatchCall(txs, activeAccount); diff --git a/packages/app/src/modals/Bond/index.tsx b/packages/app/src/modals/Bond/index.tsx index 560d12356..315b75a3f 100644 --- a/packages/app/src/modals/Bond/index.tsx +++ b/packages/app/src/modals/Bond/index.tsx @@ -111,7 +111,7 @@ export const Bond = () => { tx = pApi.tx.NominationPools.bond_extra({ extra: { type: 'FreeBalance', - value: bondAsString, + value: BigInt(bondAsString), }, }); } else if (isStaking) { diff --git a/packages/app/src/modals/ChangePoolRoles/index.tsx b/packages/app/src/modals/ChangePoolRoles/index.tsx index 5fcb9aef7..59ad7f9f9 100644 --- a/packages/app/src/modals/ChangePoolRoles/index.tsx +++ b/packages/app/src/modals/ChangePoolRoles/index.tsx @@ -37,22 +37,19 @@ export const ChangePoolRoles = () => { return tx; } - const removeOp = { type: 'Remove' }; - const root = roleEdits?.root?.newAddress - ? { type: 'Set', value: roleEdits.root.newAddress } - : removeOp; - const nominator = roleEdits?.nominator?.newAddress - ? { type: 'Set', value: roleEdits.nominator.newAddress } - : removeOp; - const bouncer = roleEdits?.bouncer?.newAddress - ? { type: 'Set', value: roleEdits.bouncer.newAddress } - : removeOp; - tx = pApi.tx.NominationPools.update_roles({ pool_id: poolId, - new_root: root, - new_nominator: nominator, - new_bouncer: bouncer, + new_root: roleEdits?.root?.newAddress + ? { type: 'Set', value: roleEdits.root.newAddress } + : { type: 'Remove', value: undefined }, + + new_nominator: roleEdits?.nominator?.newAddress + ? { type: 'Set', value: roleEdits.nominator.newAddress } + : { type: 'Remove', value: undefined }, + + new_bouncer: roleEdits?.bouncer?.newAddress + ? { type: 'Set', value: roleEdits.bouncer.newAddress } + : { type: 'Remove', value: undefined }, }); return tx; }; diff --git a/packages/app/src/modals/ClaimReward/index.tsx b/packages/app/src/modals/ClaimReward/index.tsx index c59eceaa4..56d98abb6 100644 --- a/packages/app/src/modals/ClaimReward/index.tsx +++ b/packages/app/src/modals/ClaimReward/index.tsx @@ -57,7 +57,9 @@ export const ClaimReward = () => { } if (claimType === 'bond') { - tx = pApi.tx.NominationPools.bond_extra({ extra: { type: 'Rewards' } }); + tx = pApi.tx.NominationPools.bond_extra({ + extra: { type: 'Rewards', value: undefined }, + }); } else { tx = pApi.tx.NominationPools.claim_payout(); } diff --git a/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx b/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx index ddd2f11d0..2767f8098 100644 --- a/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/RenamePool/index.tsx @@ -61,7 +61,7 @@ export const RenamePool = ({ // tx to submit const getTx = () => { const { pApi } = ApiController.get(network); - if (!valid || !pApi) { + if (!valid || !pApi || !poolId) { return null; } return pApi.tx.NominationPools.set_metadata({ diff --git a/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx b/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx index aebd963b3..aa427e287 100644 --- a/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/SetClaimPermission/index.tsx @@ -62,12 +62,13 @@ export const SetClaimPermission = ({ // tx to submit. const getTx = () => { const { pApi } = ApiController.get(network); - if (!valid || !pApi) { + if (!valid || !pApi || !claimPermission) { return null; } return pApi.tx.NominationPools.set_claim_permission({ permission: { type: claimPermission, + value: undefined, }, }); }; diff --git a/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx b/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx index 5b0e83129..6a3f96974 100644 --- a/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx +++ b/packages/app/src/modals/ManagePool/Forms/SetPoolState/index.tsx @@ -91,19 +91,19 @@ export const SetPoolState = ({ case 'destroy_pool': tx = pApi.tx.NominationPools.set_state({ pool_id: poolId, - state: { type: 'Destroying' }, + state: { type: 'Destroying', value: undefined }, }); break; case 'unlock_pool': tx = pApi.tx.NominationPools.set_state({ pool_id: poolId, - state: { type: 'Open' }, + state: { type: 'Open', value: undefined }, }); break; case 'lock_pool': tx = pApi.tx.NominationPools.set_state({ pool_id: poolId, - state: { type: 'Blocked' }, + state: { type: 'Blocked', value: undefined }, }); break; default: diff --git a/packages/app/src/modals/UnlockChunks/Forms.tsx b/packages/app/src/modals/UnlockChunks/Forms.tsx index 869617ef6..8c8688f50 100644 --- a/packages/app/src/modals/UnlockChunks/Forms.tsx +++ b/packages/app/src/modals/UnlockChunks/Forms.tsx @@ -84,13 +84,15 @@ export const Forms = forwardRef( num_slashing_spans: historyDepth.toNumber(), }); } else if (task === 'withdraw' && isPooling && activePool) { - tx = pApi.tx.NominationPools.withdraw_unbonded({ - member_account: { - type: 'Id', - value: activeAccount, - }, - num_slashing_spans: historyDepth.toNumber(), - }); + if (activeAccount) { + tx = pApi.tx.NominationPools.withdraw_unbonded({ + member_account: { + type: 'Id', + value: activeAccount, + }, + num_slashing_spans: historyDepth.toNumber(), + }); + } } return tx; }; diff --git a/packages/app/src/modals/UpdatePayee/index.tsx b/packages/app/src/modals/UpdatePayee/index.tsx index 2d70855e6..661596a16 100644 --- a/packages/app/src/modals/UpdatePayee/index.tsx +++ b/packages/app/src/modals/UpdatePayee/index.tsx @@ -75,19 +75,24 @@ export const UpdatePayee = () => { const getTx = () => { const { pApi } = ApiController.get(network); let tx = null; - if (!pApi) { + if (!pApi || !selected.destination) { + return tx; + } + + if (selected.destination === 'Account' && !selected.account) { return tx; } - const payeeToSubmit = !isComplete() - ? { type: 'Staked' } - : selected.destination === 'Account' - ? { - type: 'Account', - value: selected.account, - } - : { type: selected.destination }; - tx = pApi.tx.Staking.set_payee({ payee: payeeToSubmit }); + tx = pApi.tx.Staking.set_payee({ + payee: !isComplete() + ? { type: 'Staked', value: undefined } + : selected.destination === 'Account' + ? { + type: 'Account', + value: selected.account as string, + } + : { type: selected.destination, value: undefined }, + }); return tx; }; diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index d4a69fe68..527216ae3 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -20,6 +20,7 @@ import { getSmProvider } from 'polkadot-api/sm-provider'; import { startFromWorker } from 'polkadot-api/smoldot/from-worker'; import SmWorker from 'polkadot-api/smoldot/worker?worker'; import { getLightClientMetadata } from 'config/util'; +import { dot } from '@polkadot-api/descriptors'; export class Api { // The network name associated with this Api instance. @@ -88,7 +89,7 @@ export class Api { this.dispatchEvent(this.ensureEventStatus('connecting')); // Initialise PAPI API. - this.#pApi = this.#papiClient.getUnsafeApi(); + this.#pApi = this.#papiClient.getTypedApi(dot); // Fetch chain spec and metadata from PAPI client. await this.fetchChainSpec(); diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index efbea3b02..043e69b82 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -1,8 +1,9 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { UnsafeApi } from 'polkadot-api'; import type { NetworkName, SystemChainId } from 'types'; +import type { IDescriptors } from '@polkadot-api/descriptors'; +import type { TypedApi } from 'polkadot-api'; export interface APIConfig { type: ConnectionType; @@ -37,7 +38,7 @@ export type PapiReadyEvent = PapiChainSpec & { chainType: string; }; -export type PapiApi = UnsafeApi; +export type PapiApi = TypedApi; export type ConnectionType = 'ws' | 'sc'; diff --git a/packages/app/src/model/ErasStakersPaged/index.tsx b/packages/app/src/model/ErasStakersPaged/index.tsx index 144800800..9eefda7db 100644 --- a/packages/app/src/model/ErasStakersPaged/index.tsx +++ b/packages/app/src/model/ErasStakersPaged/index.tsx @@ -10,7 +10,7 @@ export class ErasStakersPaged { this.#pApi = pApi; } - async fetch(era: string, validator: string) { + async fetch(era: number, validator: string) { return await this.#pApi.query.Staking.ErasStakersPaged.getEntries( era, validator, diff --git a/packages/app/src/model/Query/ClaimedRewards/index.ts b/packages/app/src/model/Query/ClaimedRewards/index.ts index 6fabb0332..1d3d39e5e 100644 --- a/packages/app/src/model/Query/ClaimedRewards/index.ts +++ b/packages/app/src/model/Query/ClaimedRewards/index.ts @@ -5,12 +5,10 @@ import type { PapiApi } from 'model/Api/types'; export class ClaimedRewards { #pApi: PapiApi; - - #era: string; - + #era: number; #address: string; - constructor(pApi: PapiApi, era: string, address: string) { + constructor(pApi: PapiApi, era: number, address: string) { this.#pApi = pApi; this.#era = era; this.#address = address; diff --git a/packages/app/src/model/Query/Era/index.ts b/packages/app/src/model/Query/Era/index.ts index 5100ced58..2c5c91699 100644 --- a/packages/app/src/model/Query/Era/index.ts +++ b/packages/app/src/model/Query/Era/index.ts @@ -12,28 +12,30 @@ export class Era { } async fetch() { - let result; + let era = { + start: 0n, + index: 0, + }; + try { - const { index, start } = - await this.#pApi.query.Staking.ActiveEra.getValue({ - at: 'best', - }); - - result = { - start, - index, - }; + const result = await this.#pApi.query.Staking.ActiveEra.getValue({ + at: 'best', + }); + + if (result) { + era = { + start: result?.start || 0n, + index: result.index, + }; + } } catch (e) { - result = { - start: 0, - index: 0, - }; + // Silent fail. } // Store active era as BigNumbers. const activeEra = { - index: new BigNumber(result.index), - start: new BigNumber(result.start), + start: new BigNumber(era.start.toString()), + index: new BigNumber(era.index), }; // Get previous era. diff --git a/packages/app/src/model/Query/ErasRewardPoints/index.ts b/packages/app/src/model/Query/ErasRewardPoints/index.ts index 6ac3b3075..47452cc24 100644 --- a/packages/app/src/model/Query/ErasRewardPoints/index.ts +++ b/packages/app/src/model/Query/ErasRewardPoints/index.ts @@ -6,9 +6,9 @@ import type { PapiApi } from 'model/Api/types'; export class ErasRewardPoints { #pApi: PapiApi; - #era: string; + #era: number; - constructor(pApi: PapiApi, era: string) { + constructor(pApi: PapiApi, era: number) { this.#pApi = pApi; this.#era = era; } diff --git a/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts index 2c02adcc1..5c094fce1 100644 --- a/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts +++ b/packages/app/src/model/Query/ErasRewardPointsMulti/index.ts @@ -6,9 +6,9 @@ import type { PapiApi } from 'model/Api/types'; export class ErasRewardPointsMulti { #pApi: PapiApi; - #eras: [string][]; + #eras: [number][]; - constructor(pApi: PapiApi, eras: [string][]) { + constructor(pApi: PapiApi, eras: [number][]) { this.#pApi = pApi; this.#eras = eras; } diff --git a/packages/app/src/model/Query/ErasStakersOverview/index.tsx b/packages/app/src/model/Query/ErasStakersOverview/index.tsx index cae72128e..37ad396f2 100644 --- a/packages/app/src/model/Query/ErasStakersOverview/index.tsx +++ b/packages/app/src/model/Query/ErasStakersOverview/index.tsx @@ -10,7 +10,7 @@ export class ErasStakersOverview { this.#pApi = pApi; } - async fetch(era: string) { + async fetch(era: number) { return await this.#pApi.query.Staking.ErasStakersOverview.getEntries(era, { at: 'best', }); diff --git a/packages/app/src/model/Query/ErasValidatorReward/index.ts b/packages/app/src/model/Query/ErasValidatorReward/index.ts index bcdc54daf..3ba3c1743 100644 --- a/packages/app/src/model/Query/ErasValidatorReward/index.ts +++ b/packages/app/src/model/Query/ErasValidatorReward/index.ts @@ -5,10 +5,9 @@ import type { PapiApi } from 'model/Api/types'; export class ErasValidatorReward { #pApi: PapiApi; + #era: number; - #era: string; - - constructor(pApi: PapiApi, era: string) { + constructor(pApi: PapiApi, era: number) { this.#pApi = pApi; this.#era = era; } diff --git a/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts index f57bc15e0..ada87a6fc 100644 --- a/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts +++ b/packages/app/src/model/Query/ErasValidatorRewardMulti/index.ts @@ -5,10 +5,9 @@ import type { PapiApi } from 'model/Api/types'; export class ErasValidatorReward { #pApi: PapiApi; + #eras: [number][]; - #eras: [string][]; - - constructor(pApi: PapiApi, eras: [string][]) { + constructor(pApi: PapiApi, eras: [number][]) { this.#pApi = pApi; this.#eras = eras; } diff --git a/packages/app/src/model/Query/NetworkMeta/index.ts b/packages/app/src/model/Query/NetworkMeta/index.ts index 80b48eabe..61fd9d978 100644 --- a/packages/app/src/model/Query/NetworkMeta/index.ts +++ b/packages/app/src/model/Query/NetworkMeta/index.ts @@ -62,16 +62,16 @@ export class NetworkMeta { this.#pApi.query.Staking.MaxValidatorsCount.getValue(at), this.#pApi.query.Staking.ValidatorCount.getValue(at), this.#pApi.query.Staking.ErasValidatorReward.getValue( - previousEra.toString(), + previousEra.toNumber(), at ), this.#pApi.query.Staking.ErasTotalStake.getValue( - previousEra.toString(), + previousEra.toNumber(), at ), this.#pApi.query.Staking.MinNominatorBond.getValue(at), this.#pApi.query.Staking.ErasTotalStake.getValue( - activeEra.index.toString(), + activeEra.index.toNumber(), at ), ]); @@ -116,9 +116,9 @@ export class NetworkMeta { }, stakingMetrics: { totalValidators: stringToBn(counterForValidators.toString()), - maxValidatorsCount: stringToBn(maxValidatorsCount.toString()), + maxValidatorsCount: stringToBn(maxValidatorsCount?.toString() || '0'), validatorCount: stringToBn(validatorCount.toString()), - lastReward: stringToBn(prevErasValidatorReward.toString()), + lastReward: stringToBn(prevErasValidatorReward?.toString() || '0'), lastTotalStake: stringToBn(prevEraErasTotalStake.toString()), minNominatorBond: stringToBn(minNominatorBond.toString()), totalStaked: stringToBn(activeEraErasTotalStake.toString()), diff --git a/packages/app/src/model/Query/ParaSessionAccounts/index.ts b/packages/app/src/model/Query/ParaSessionAccounts/index.ts index 1aedab661..26d52111f 100644 --- a/packages/app/src/model/Query/ParaSessionAccounts/index.ts +++ b/packages/app/src/model/Query/ParaSessionAccounts/index.ts @@ -5,10 +5,9 @@ import type { PapiApi } from 'model/Api/types'; export class ParaSessionAccounts { #pApi: PapiApi; + #session: number; - #session: string; - - constructor(pApi: PapiApi, session: string) { + constructor(pApi: PapiApi, session: number) { this.#pApi = pApi; this.#session = session; } @@ -20,7 +19,10 @@ export class ParaSessionAccounts { this.#session, { at: 'best' } ); - return result; + + if (result) { + return result; + } } catch (e) { // Silent fail } diff --git a/packages/app/src/model/Query/ValidatorPrefs/index.ts b/packages/app/src/model/Query/ValidatorPrefs/index.ts index 086a61949..c80aa2c72 100644 --- a/packages/app/src/model/Query/ValidatorPrefs/index.ts +++ b/packages/app/src/model/Query/ValidatorPrefs/index.ts @@ -5,12 +5,10 @@ import type { PapiApi } from 'model/Api/types'; export class ValidatorPrefs { #pApi: PapiApi; - - #era: string; - + #era: number; #address: string; - constructor(pApi: PapiApi, era: string, address: string) { + constructor(pApi: PapiApi, era: number, address: string) { this.#pApi = pApi; this.#era = era; this.#address = address; diff --git a/packages/app/src/model/Subscribe/ActiveEra/index.ts b/packages/app/src/model/Subscribe/ActiveEra/index.ts index 935fa28f2..632bd01b1 100644 --- a/packages/app/src/model/Subscribe/ActiveEra/index.ts +++ b/packages/app/src/model/Subscribe/ActiveEra/index.ts @@ -38,8 +38,8 @@ export class ActiveEra implements Unsubscribable { ).subscribe((activeEra) => { // Store active era. this.activeEra = { - index: new BigNumber(activeEra.index.toString()), - start: new BigNumber(activeEra.start.toString()), + index: new BigNumber(activeEra?.index.toString() || 0), + start: new BigNumber(activeEra?.start?.toString() || 0), }; // Unsubscribe to staking metrics if it exists. diff --git a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts index 2283f4b86..2242dab0b 100644 --- a/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts +++ b/packages/app/src/model/Subscribe/ActivePoolAccount/index.ts @@ -52,11 +52,11 @@ export class ActivePoolAccount implements Unsubscribable { const sub = combineLatest([ pApi.query.NominationPools.BondedPools.watchValue( - this.pool.id, + Number(this.pool.id), bestOrFinalized ), pApi.query.NominationPools.RewardPools.watchValue( - this.pool.id, + Number(this.pool.id), bestOrFinalized ), pApi.query.System.Account.watchValue( diff --git a/packages/app/src/model/Subscribe/Bonded/index.tsx b/packages/app/src/model/Subscribe/Bonded/index.tsx index 25281319f..6a886a2ce 100644 --- a/packages/app/src/model/Subscribe/Bonded/index.tsx +++ b/packages/app/src/model/Subscribe/Bonded/index.tsx @@ -38,7 +38,7 @@ export class Bonded implements Unsubscribable { ).subscribe((controller) => { const account: BondedAccount = { address: this.#address, - bonded: controller || null, + bonded: controller || undefined, }; // Send bonded account to UI. diff --git a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts index 7248c5963..202c791b2 100644 --- a/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts +++ b/packages/app/src/model/Subscribe/FastUnstakeConfig/index.ts @@ -34,7 +34,10 @@ export class FastUnstakeConfig implements Unsubscribable { pApi.query.FastUnstake.CounterForQueue.watchValue(bestOrFinalized), ]).subscribe(([head, counterForQueue]) => { const config: FastUnstakeConfigResult = { - head, + head: head || { + stashes: [], + checked: [], + }, counterForQueue, }; diff --git a/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts index bb99f7509..8bd43e81d 100644 --- a/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts +++ b/packages/app/src/model/Subscribe/FastUnstakeQueue/index.ts @@ -35,7 +35,7 @@ export class FastUnstakeQueue implements Unsubscribable { this.#address, bestOrFinalized ).subscribe((queue) => { - this.queue = queue; + this.queue = queue || 0n; document.dispatchEvent( new CustomEvent('new-fast-unstake-deposit', { diff --git a/packages/app/src/model/Subscribe/PoolMembersMulti/index.ts b/packages/app/src/model/Subscribe/PoolMembersMulti/index.ts index cb032ac4e..fa922a9d2 100644 --- a/packages/app/src/model/Subscribe/PoolMembersMulti/index.ts +++ b/packages/app/src/model/Subscribe/PoolMembersMulti/index.ts @@ -40,18 +40,28 @@ export class PoolMembersMulti implements Unsubscribable { ) ) ).subscribe((results) => { - const formatted = results.map((result) => ({ - lastRecordedRewardCounter: - result.last_recorded_reward_counter.toString(), - points: result.points.toString(), - poolId: result.pool_id.toString(), - unbondingEras: Object.fromEntries( - result.unbonding_eras.map(([key, value]: [number, bigint]) => [ - key.toString(), - value.toString(), - ]) - ), - })); + const formatted = results + .map((result) => { + if (!result) { + return undefined; + } + + return { + lastRecordedRewardCounter: + result.last_recorded_reward_counter.toString(), + points: result.points.toString(), + poolId: result.pool_id.toString(), + unbondingEras: Object.fromEntries( + result.unbonding_eras.map( + ([key, value]: [number, bigint]) => [ + key.toString(), + value.toString(), + ] + ) + ), + }; + }) + .filter((result) => result !== undefined); const detail: PoolMemberBatchEvent = { key: this.#key, diff --git a/packages/app/src/model/Subscribe/PoolsConfig/index.ts b/packages/app/src/model/Subscribe/PoolsConfig/index.ts index a15885ac3..3ac1f1127 100644 --- a/packages/app/src/model/Subscribe/PoolsConfig/index.ts +++ b/packages/app/src/model/Subscribe/PoolsConfig/index.ts @@ -64,8 +64,9 @@ export class PoolsConfig implements Unsubscribable { globalMaxCommission, ]) => { // Format globalMaxCommission from a perbill to a percent. - const globalMaxCommissionAsPercent = - BigInt(globalMaxCommission) / 1000000n; + const globalMaxCommissionAsPercent = globalMaxCommission + ? BigInt(globalMaxCommission) / 1000000n + : 100n; // Format max pool members to be a BigNumber, or null if it's not set. const maxPoolMembers = maxPoolMembersRaw diff --git a/packages/app/src/model/Subscribe/StakingMetrics/index.ts b/packages/app/src/model/Subscribe/StakingMetrics/index.ts index 638ac9bbb..8c0bf78c7 100644 --- a/packages/app/src/model/Subscribe/StakingMetrics/index.ts +++ b/packages/app/src/model/Subscribe/StakingMetrics/index.ts @@ -45,16 +45,16 @@ export class StakingMetrics implements Unsubscribable { pApi.query.Staking.MaxValidatorsCount.watchValue(bestOrFinalized), pApi.query.Staking.ValidatorCount.watchValue(bestOrFinalized), pApi.query.Staking.ErasValidatorReward.watchValue( - this.#previousEra.toString(), + this.#previousEra.toNumber(), bestOrFinalized ), pApi.query.Staking.ErasTotalStake.watchValue( - this.#previousEra.toString(), + this.#previousEra.toNumber(), bestOrFinalized ), pApi.query.Staking.MinNominatorBond.watchValue(bestOrFinalized), pApi.query.Staking.ErasTotalStake.watchValue( - this.#activeEra.index.toString(), + this.#activeEra.index.toNumber(), bestOrFinalized ), pApi.query.Staking.CounterForNominators.watchValue(bestOrFinalized), @@ -71,9 +71,11 @@ export class StakingMetrics implements Unsubscribable { ]) => { const stakingMetrics = { totalValidators: stringToBn(counterForValidators.toString()), - maxValidatorsCount: stringToBn(maxValidatorsCount.toString()), + maxValidatorsCount: stringToBn( + maxValidatorsCount?.toString() || '0' + ), validatorCount: stringToBn(validatorCount.toString()), - lastReward: stringToBn(erasValidatorReward.toString()), + lastReward: stringToBn(erasValidatorReward?.toString() || '0'), lastTotalStake: stringToBn(lastTotalStake.toString()), minNominatorBond: stringToBn(minNominatorBond.toString()), totalStaked: stringToBn(totalStaked.toString()), From 172a7a8d561a2732a3297485baa500edff9eb313 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 20:59:50 +0700 Subject: [PATCH 80/84] back to unsafe --- packages/app/src/model/Api/index.ts | 3 +-- packages/app/src/model/Api/types.ts | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/app/src/model/Api/index.ts b/packages/app/src/model/Api/index.ts index 527216ae3..d4a69fe68 100644 --- a/packages/app/src/model/Api/index.ts +++ b/packages/app/src/model/Api/index.ts @@ -20,7 +20,6 @@ import { getSmProvider } from 'polkadot-api/sm-provider'; import { startFromWorker } from 'polkadot-api/smoldot/from-worker'; import SmWorker from 'polkadot-api/smoldot/worker?worker'; import { getLightClientMetadata } from 'config/util'; -import { dot } from '@polkadot-api/descriptors'; export class Api { // The network name associated with this Api instance. @@ -89,7 +88,7 @@ export class Api { this.dispatchEvent(this.ensureEventStatus('connecting')); // Initialise PAPI API. - this.#pApi = this.#papiClient.getTypedApi(dot); + this.#pApi = this.#papiClient.getUnsafeApi(); // Fetch chain spec and metadata from PAPI client. await this.fetchChainSpec(); diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index 043e69b82..6a8dc187a 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -2,8 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import type { NetworkName, SystemChainId } from 'types'; -import type { IDescriptors } from '@polkadot-api/descriptors'; -import type { TypedApi } from 'polkadot-api'; +import type { UnsafeApi } from 'polkadot-api'; export interface APIConfig { type: ConnectionType; @@ -38,7 +37,7 @@ export type PapiReadyEvent = PapiChainSpec & { chainType: string; }; -export type PapiApi = TypedApi; +export type PapiApi = UnsafeApi; export type ConnectionType = 'ws' | 'sc'; From 9bc5ba79f4e1c903839e8056cce9d3c979fd1d62 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 21:21:12 +0700 Subject: [PATCH 81/84] wallet connect fix --- packages/app/src/contexts/WalletConnect/index.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/app/src/contexts/WalletConnect/index.tsx b/packages/app/src/contexts/WalletConnect/index.tsx index b19a4adea..24ea783fe 100644 --- a/packages/app/src/contexts/WalletConnect/index.tsx +++ b/packages/app/src/contexts/WalletConnect/index.tsx @@ -257,7 +257,7 @@ export const WalletConnectProvider = ({ } const topic = wcProvider.current.session.topic; const caip = `polkadot:${genesisHash.substring(2).substring(0, 32)}`; - const request = { + return await wcProvider.current.client.request({ chainId: caip, topic, request: { @@ -267,11 +267,7 @@ export const WalletConnectProvider = ({ transactionPayload: payload, }, }, - }; - const result: { signature: string } = - await wcProvider.current.client.request(request); - - return { signature: result?.signature || '0x' }; + }); }; const fetchAddresses = async (): Promise => { From d52a11e839ea31f452d6552b0acbe525aed3ae05 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 22 Nov 2024 21:24:50 +0700 Subject: [PATCH 82/84] add comment --- packages/app/src/hooks/useSubmitExtrinsic/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx index 55ea78bb1..3b2c0c2f4 100644 --- a/packages/app/src/hooks/useSubmitExtrinsic/index.tsx +++ b/packages/app/src/hooks/useSubmitExtrinsic/index.tsx @@ -283,6 +283,7 @@ export const useSubmitExtrinsic = ({ fromRef.current, signWcTx, // Signing bytes not currently being used. + // FIXME: Can implement, albeit won't be used. async () => ({ id: 0, signature: '0x', From 716a1468977b85f0619883548c5c35ac8eb253d7 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 26 Nov 2024 16:42:49 +0700 Subject: [PATCH 83/84] more linting --- packages/app/src/hooks/useCreatePoolAccounts/index.tsx | 2 +- packages/app/src/library/Signers/LedgerSigner.ts | 6 +++--- packages/app/src/model/Api/types.ts | 2 +- packages/app/src/model/Subscribe/ActiveEra/index.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app/src/hooks/useCreatePoolAccounts/index.tsx b/packages/app/src/hooks/useCreatePoolAccounts/index.tsx index c7285010d..16ccd601d 100644 --- a/packages/app/src/hooks/useCreatePoolAccounts/index.tsx +++ b/packages/app/src/hooks/useCreatePoolAccounts/index.tsx @@ -1,11 +1,11 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only +import { AccountId } from '@polkadot-api/substrate-bindings'; import { bnToU8a, stringToU8a, u8aConcat } from '@polkadot/util'; import BigNumber from 'bignumber.js'; import { BN } from 'bn.js'; import { useApi } from 'contexts/Api'; -import { AccountId } from '@polkadot-api/substrate-bindings'; export const useCreatePoolAccounts = () => { const { diff --git a/packages/app/src/library/Signers/LedgerSigner.ts b/packages/app/src/library/Signers/LedgerSigner.ts index ba544d70c..153b41de5 100644 --- a/packages/app/src/library/Signers/LedgerSigner.ts +++ b/packages/app/src/library/Signers/LedgerSigner.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: GPL-3.0-only import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata'; -import type { PolkadotSigner } from 'polkadot-api'; -import { mergeUint8 } from 'polkadot-api/utils'; +import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; import type { V15 } from '@polkadot-api/substrate-bindings'; import { decAnyMetadata } from '@polkadot-api/substrate-bindings'; +import type { PolkadotSigner } from 'polkadot-api'; +import { mergeUint8 } from 'polkadot-api/utils'; import { Ledger } from '../../contexts/LedgerHardware/static/ledger'; -import { createV4Tx, getSignBytes } from '@polkadot-api/signers-common'; import { getExtraSignedExtensions } from './util'; export class LedgerSigner { diff --git a/packages/app/src/model/Api/types.ts b/packages/app/src/model/Api/types.ts index 6a8dc187a..efbea3b02 100644 --- a/packages/app/src/model/Api/types.ts +++ b/packages/app/src/model/Api/types.ts @@ -1,8 +1,8 @@ // Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors // SPDX-License-Identifier: GPL-3.0-only -import type { NetworkName, SystemChainId } from 'types'; import type { UnsafeApi } from 'polkadot-api'; +import type { NetworkName, SystemChainId } from 'types'; export interface APIConfig { type: ConnectionType; diff --git a/packages/app/src/model/Subscribe/ActiveEra/index.ts b/packages/app/src/model/Subscribe/ActiveEra/index.ts index 632bd01b1..c0d6d61ed 100644 --- a/packages/app/src/model/Subscribe/ActiveEra/index.ts +++ b/packages/app/src/model/Subscribe/ActiveEra/index.ts @@ -7,9 +7,9 @@ import type { APIActiveEra } from 'contexts/Api/types'; import { ApiController } from 'controllers/Api'; import { SubscriptionsController } from 'controllers/Subscriptions'; import type { Unsubscribable } from 'controllers/Subscriptions/types'; +import type { Subscription } from 'rxjs'; import type { NetworkName } from 'types'; import { StakingMetrics } from '../StakingMetrics'; -import type { Subscription } from 'rxjs'; export class ActiveEra implements Unsubscribable { // The associated network for this instance. From dd5aa19c8ea9b2af207ffa90ebda4d8a67e55190 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 26 Nov 2024 19:19:27 +0700 Subject: [PATCH 84/84] yarn --- yarn.lock | 760 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 425 insertions(+), 335 deletions(-) diff --git a/yarn.lock b/yarn.lock index fe073ef6e..f57a63be7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1544,9 +1544,9 @@ __metadata: linkType: hard "@noble/ciphers@npm:^1.0.0": - version: 1.0.0 - resolution: "@noble/ciphers@npm:1.0.0" - checksum: 10c0/6c04d6e9d10a922fff170efc44622c95a25fb817f4b593e0f150dd27599576f3fe3c5b61eb02054b22d1507e3839879ddd5acb2d2acf8efbea4efab99bbcd333 + version: 1.1.0 + resolution: "@noble/ciphers@npm:1.1.0" + checksum: 10c0/18313ccdeb8337d8739e3178cc9c1c30866453b9f398d0b195b7d4b336af8ebf334da2c2c7afa83f2bcd2c57243e2686354d41bb72090e586131f421518b5be1 languageName: node linkType: hard @@ -1559,7 +1559,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.6.0, @noble/curves@npm:^1.3.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.6.0": +"@noble/curves@npm:1.6.0, @noble/curves@npm:~1.6.0": version: 1.6.0 resolution: "@noble/curves@npm:1.6.0" dependencies: @@ -1568,6 +1568,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:^1.3.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.7.0": + version: 1.7.0 + resolution: "@noble/curves@npm:1.7.0" + dependencies: + "@noble/hashes": "npm:1.6.0" + checksum: 10c0/3317ec9b7699d2476707a89ceb3ddce60e69bac287561a31dd533669408633e093860fea5067eb9c54e5a7ced0705da1cba8859b6b1e0c48d3afff55fe2e77d0 + languageName: node + linkType: hard + "@noble/hashes@npm:1.4.0, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" @@ -1575,13 +1584,27 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.5.0": +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:~1.5.0": version: 1.5.0 resolution: "@noble/hashes@npm:1.5.0" checksum: 10c0/1b46539695fbfe4477c0822d90c881a04d4fa2921c08c552375b444a48cac9930cb1ee68de0a3c7859e676554d0f3771999716606dc4d8f826e414c11692cdd9 languageName: node linkType: hard +"@noble/hashes@npm:1.6.0": + version: 1.6.0 + resolution: "@noble/hashes@npm:1.6.0" + checksum: 10c0/e7e75898257fb36d933935fcdf1cc67ca7c083eb7b2411aa57fde7eb494c2cea0bec03686462032e25d5b0e1e4ab7357d1afb6718f6a68515db1f392141e9f14 + languageName: node + linkType: hard + +"@noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.6.0": + version: 1.6.1 + resolution: "@noble/hashes@npm:1.6.1" + checksum: 10c0/27643cd8b551bc933b57cc29aa8c8763d586552fc4c3e06ecf7897f55be3463c0c9dff7f6ebacd88e5ce6d0cdb5415ca4874d0cf4359b5ea4a85be21ada03aab + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1807,26 +1830,26 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/cli@npm:0.9.20": - version: 0.9.20 - resolution: "@polkadot-api/cli@npm:0.9.20" +"@polkadot-api/cli@npm:0.9.21": + version: 0.9.21 + resolution: "@polkadot-api/cli@npm:0.9.21" dependencies: "@commander-js/extra-typings": "npm:^12.1.0" - "@polkadot-api/codegen": "npm:0.12.8" - "@polkadot-api/ink-contracts": "npm:0.2.1" + "@polkadot-api/codegen": "npm:0.12.9" + "@polkadot-api/ink-contracts": "npm:0.2.2" "@polkadot-api/json-rpc-provider": "npm:0.0.4" - "@polkadot-api/known-chains": "npm:0.5.7" - "@polkadot-api/metadata-compatibility": "npm:0.1.11" - "@polkadot-api/observable-client": "npm:0.6.2" + "@polkadot-api/known-chains": "npm:0.5.8" + "@polkadot-api/metadata-compatibility": "npm:0.1.12" + "@polkadot-api/observable-client": "npm:0.6.3" "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" "@polkadot-api/sm-provider": "npm:0.1.7" "@polkadot-api/smoldot": "npm:0.3.7" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/substrate-client": "npm:0.3.0" "@polkadot-api/utils": "npm:0.1.2" "@polkadot-api/wasm-executor": "npm:^0.1.2" "@polkadot-api/ws-provider": "npm:0.3.6" - "@types/node": "npm:^22.9.0" + "@types/node": "npm:^22.9.1" commander: "npm:^12.1.0" execa: "npm:^9.5.1" fs.promises.exists: "npm:^1.1.4" @@ -1840,32 +1863,32 @@ __metadata: bin: papi: dist/main.js polkadot-api: dist/main.js - checksum: 10c0/579b3dbc6c291a80aa4963745db41f2879727c4c811282bea8ba07e45ee130a524f2b9bcd142b9af3aed68740dbdf798a0f9e15a2167d94cf35459f96b7855da + checksum: 10c0/0d0e26e84998c6ddf5a13e1f701af5204c35ab06b7cdc3f8b1221cedd361922a5db904fbb32f0bb1416dd3f6570a1d36a6d7c94101612605a93baea77f32794a languageName: node linkType: hard -"@polkadot-api/codegen@npm:0.12.8": - version: 0.12.8 - resolution: "@polkadot-api/codegen@npm:0.12.8" +"@polkadot-api/codegen@npm:0.12.9": + version: 0.12.9 + resolution: "@polkadot-api/codegen@npm:0.12.9" dependencies: - "@polkadot-api/ink-contracts": "npm:0.2.1" - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/metadata-compatibility": "npm:0.1.11" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/ink-contracts": "npm:0.2.2" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/metadata-compatibility": "npm:0.1.12" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/18f1be81133ff07eb2ea6931bb1091ee5fde19f6943042d98b2126c168d870740202dc236c4783767b00ff35e40040e09ebc28d12ed4075228bfdadbf5b9d3f0 + checksum: 10c0/9dce88598104cbe7f237860bbc31485e9ab84eac761b9a8ab0521f075b34915233ea855cf9245ca1c013e933ef8fa7aaecac376513b7e1c829f7b62f035c963e languageName: node linkType: hard -"@polkadot-api/ink-contracts@npm:0.2.1": - version: 0.2.1 - resolution: "@polkadot-api/ink-contracts@npm:0.2.1" +"@polkadot-api/ink-contracts@npm:0.2.2": + version: 0.2.2 + resolution: "@polkadot-api/ink-contracts@npm:0.2.2" dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" scale-ts: "npm:^1.6.1" - checksum: 10c0/a6df3a780d41d577832576790beb355132d86f8089d1886fc5e78250e3de3556ade9571e71f893b93eebb3476f8c82804eb477d626ed26c993dc2ea3b4d9ef20 + checksum: 10c0/86ab565f94205f47887917b2193c5825c0f27a027442a2240a64e2e6885743334b4c526fb896cb6ad411311ae4c9a122fc37e98434b41aa895300e633d6347fb languageName: node linkType: hard @@ -1897,10 +1920,10 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/known-chains@npm:0.5.7": - version: 0.5.7 - resolution: "@polkadot-api/known-chains@npm:0.5.7" - checksum: 10c0/5ba499e58f0066346a48bda459b23570c849fb267a10c8a23494661c563ea7763b560d4d3fa95283b9cb6767d1a1af651d23c94ac7550050d2f5f874569e29d1 +"@polkadot-api/known-chains@npm:0.5.8": + version: 0.5.8 + resolution: "@polkadot-api/known-chains@npm:0.5.8" + checksum: 10c0/2f0dfd5f610927f00f41245113952c581cfa2b3fc0e1e29578ac52ca87b44a24aad5d10b76e3a79022cd6da46bb02048c854537004aee6da3df1a61bd53e0c72 languageName: node linkType: hard @@ -1914,13 +1937,13 @@ __metadata: linkType: hard "@polkadot-api/merkleize-metadata@npm:^1.1.4": - version: 1.1.9 - resolution: "@polkadot-api/merkleize-metadata@npm:1.1.9" + version: 1.1.10 + resolution: "@polkadot-api/merkleize-metadata@npm:1.1.10" dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/096ec12790095e6f11e89585caaa061195bcf4724bf8ddec478cf03513c3dc667e8d28ee0aebdbb715674b9763941432d59ab6cbbec4f66a1b12bc8ee3b16f8a + checksum: 10c0/77f4e8cd7537228806e5d4d01f7bd385d3d46b633ab84ca8a7a8e0293a4b86487175081a6d0dd204f0ca9050dfb713e1354546d4f30c764b44875a9e1082a34d languageName: node linkType: hard @@ -1934,37 +1957,37 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/metadata-builders@npm:0.9.1": - version: 0.9.1 - resolution: "@polkadot-api/metadata-builders@npm:0.9.1" +"@polkadot-api/metadata-builders@npm:0.9.2": + version: 0.9.2 + resolution: "@polkadot-api/metadata-builders@npm:0.9.2" dependencies: - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/768c14b578fe2d25c58904479a6d37da38f86e84fe11e01d16a86d56640138a2efb67c0706578e871de8529106491b9299173422d29259b045387cf7a35c6210 + checksum: 10c0/2a5171e6659f0d99d247d4483ce7832bd803f563ce5eac59dbecd40d84eee4f0ff31ccbfdcd9c85fd805b5aeb4d865975f87ceba6dbb0de04753144988e8074f languageName: node linkType: hard -"@polkadot-api/metadata-compatibility@npm:0.1.11": - version: 0.1.11 - resolution: "@polkadot-api/metadata-compatibility@npm:0.1.11" +"@polkadot-api/metadata-compatibility@npm:0.1.12": + version: 0.1.12 + resolution: "@polkadot-api/metadata-compatibility@npm:0.1.12" dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" - checksum: 10c0/66397a323d272ea33fdf71fffd0ed4085d5a7d1333b07af21932cf19911cf8271905cc8489a7d40b67319ea83b5d5c2c056e692a2059c99ee22bec440167ae07 + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" + checksum: 10c0/b3aa63d3c9590cb4eff58825b5276bf523715f63277c5e2b048c2c5f7af8855fc2507b6a3142dddc8bc55188c8e423e707e35dfb2145154c84af78e0e1b0f4bb languageName: node linkType: hard -"@polkadot-api/observable-client@npm:0.6.2": - version: 0.6.2 - resolution: "@polkadot-api/observable-client@npm:0.6.2" +"@polkadot-api/observable-client@npm:0.6.3": + version: 0.6.3 + resolution: "@polkadot-api/observable-client@npm:0.6.3" dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" peerDependencies: "@polkadot-api/substrate-client": 0.3.0 rxjs: ">=7.8.0" - checksum: 10c0/dd5e767a16c70566776ee24aee62e1c4029b72bf1952d0b26e37da65b04c36c288a50ffdaf644b999d05fcb01da9e3aed4112b8ac0ef258d9d3135b4478d1ed0 + checksum: 10c0/e5cc27b07546a78f892495c87c89e232458d0548311f3a5b55e0b2fe1745e2576575455f4af4cc0b13869d977e440f30e66476faf23dafe79996952a90320e3b languageName: node linkType: hard @@ -1982,16 +2005,16 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/pjs-signer@npm:0.6.0": - version: 0.6.0 - resolution: "@polkadot-api/pjs-signer@npm:0.6.0" +"@polkadot-api/pjs-signer@npm:0.6.1": + version: 0.6.1 + resolution: "@polkadot-api/pjs-signer@npm:0.6.1" dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/metadata-builders": "npm:0.9.2" "@polkadot-api/polkadot-signer": "npm:0.1.6" - "@polkadot-api/signers-common": "npm:0.1.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/signers-common": "npm:0.1.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/efe86166d5f7f62cb9c94f555e56d56d2f73c2bef18cfb93a0e7004e0dc67d08abe0ec75712e03cb8506615718f17e06300dc7dfe50831fbbdd7286485223a24 + checksum: 10c0/76ec7481d36a575788303ec998d7c41246728ad82af4229ff33f8dd889ca8a8f109dd043ed69d38b7d31398355e8afe8864760287ab6e3de96718c3427654127 languageName: node linkType: hard @@ -2011,28 +2034,28 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/signer@npm:0.1.10": - version: 0.1.10 - resolution: "@polkadot-api/signer@npm:0.1.10" +"@polkadot-api/signer@npm:0.1.11": + version: 0.1.11 + resolution: "@polkadot-api/signer@npm:0.1.11" dependencies: "@noble/hashes": "npm:^1.5.0" "@polkadot-api/polkadot-signer": "npm:0.1.6" - "@polkadot-api/signers-common": "npm:0.1.1" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/signers-common": "npm:0.1.2" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/25e2f5dcb02945aad2cb777e52f87a5dfcc0f0b46238717068419a9efa67f04f40de8ce05ec4ab4f046f028590f4729f2a0a019b44e8e3c8fa66a692713344de + checksum: 10c0/35048942c7dce5cec4918fd19fb6c4e8797b4a5a522ec4757f7f0db5eb9fec0e534269327dc09efb092e08c0a81f96b84133ec7a1125eeffbd1a48fd8d1cad71 languageName: node linkType: hard -"@polkadot-api/signers-common@npm:0.1.1, @polkadot-api/signers-common@npm:^0.1.1": - version: 0.1.1 - resolution: "@polkadot-api/signers-common@npm:0.1.1" +"@polkadot-api/signers-common@npm:0.1.2, @polkadot-api/signers-common@npm:^0.1.1": + version: 0.1.2 + resolution: "@polkadot-api/signers-common@npm:0.1.2" dependencies: - "@polkadot-api/metadata-builders": "npm:0.9.1" + "@polkadot-api/metadata-builders": "npm:0.9.2" "@polkadot-api/polkadot-signer": "npm:0.1.6" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/utils": "npm:0.1.2" - checksum: 10c0/17f1811e568ae3d655ae90dffdc3dd7327dc1d99673f6362f241c1e5595e188797755cb4b4bcbfae321385dadb38258b16725ea89e5de1fe8088a2dd227c9b34 + checksum: 10c0/5bbb36f0258ec37e9daf459d40252329cb721a83dd1b1867a9f90e44946ab69b2f6ec8f11cb30ff330f4222280b1a92f828648e1b4edee341779d7849e909140 languageName: node linkType: hard @@ -2070,15 +2093,15 @@ __metadata: languageName: node linkType: hard -"@polkadot-api/substrate-bindings@npm:0.9.3, @polkadot-api/substrate-bindings@npm:^0.9.3": - version: 0.9.3 - resolution: "@polkadot-api/substrate-bindings@npm:0.9.3" +"@polkadot-api/substrate-bindings@npm:0.9.4, @polkadot-api/substrate-bindings@npm:^0.9.3": + version: 0.9.4 + resolution: "@polkadot-api/substrate-bindings@npm:0.9.4" dependencies: - "@noble/hashes": "npm:^1.4.0" + "@noble/hashes": "npm:^1.5.0" "@polkadot-api/utils": "npm:0.1.2" - "@scure/base": "npm:^1.1.7" + "@scure/base": "npm:^1.1.9" scale-ts: "npm:^1.6.1" - checksum: 10c0/d0b2e9adf3b28e7aaa1f52e26fd7b200defe1b83499450227289bff0ec784e7dab334e93dc8d989e10e31d6cbb3ac561afd50f1c25e54b9ddc2be02826ca43c2 + checksum: 10c0/eb4226807a07ed8f65ae641a1973d8b0b97410392b7c8d23002c9881d4098f31e31a14157a4e4adf250afd66355406a6108995d14a72ff3dfc15b17f314d2e61 languageName: node linkType: hard @@ -2827,128 +2850,128 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.27.3" +"@rollup/rollup-android-arm-eabi@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.27.4" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-android-arm64@npm:4.27.3" +"@rollup/rollup-android-arm64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-android-arm64@npm:4.27.4" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-darwin-arm64@npm:4.27.3" +"@rollup/rollup-darwin-arm64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.27.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-darwin-x64@npm:4.27.3" +"@rollup/rollup-darwin-x64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.27.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.27.3" +"@rollup/rollup-freebsd-arm64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.27.4" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-freebsd-x64@npm:4.27.3" +"@rollup/rollup-freebsd-x64@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-freebsd-x64@npm:4.27.4" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.27.3" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.27.4" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.27.3" +"@rollup/rollup-linux-arm-musleabihf@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.27.4" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.27.3" +"@rollup/rollup-linux-arm64-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.27.4" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.27.3" +"@rollup/rollup-linux-arm64-musl@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.27.4" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.3" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.27.4" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.27.3" +"@rollup/rollup-linux-riscv64-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.27.4" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.27.3" +"@rollup/rollup-linux-s390x-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.27.4" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.27.3" +"@rollup/rollup-linux-x64-gnu@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.27.4" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.27.3" +"@rollup/rollup-linux-x64-musl@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.27.4" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.27.3" +"@rollup/rollup-win32-arm64-msvc@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.27.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.27.3" +"@rollup/rollup-win32-ia32-msvc@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.27.4" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.27.3": - version: 4.27.3 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.27.3" +"@rollup/rollup-win32-x64-msvc@npm:4.27.4": + version: 4.27.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.27.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2981,13 +3004,20 @@ __metadata: linkType: hard "@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": - version: 3.22.3 - resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.3" - checksum: 10c0/17cb09945e845556332f65785f1aa6a2d4b7c319a87f6fde48263a88e6b2aeaa6f9bb82b4f12a471d1c6a5f2d03465d2ae34a7730d790ab51349d2039aacfa37 + version: 3.22.4 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.4" + checksum: 10c0/9080ae01254571d0f62b35764a1ca4fec302627c467060fa1427127606415627bd8c3d0909e0c0410b64110c445b6f299f5b0e9228268e1213dd75f21a6139ef + languageName: node + linkType: hard + +"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.3, @scure/base@npm:^1.1.5, @scure/base@npm:^1.1.7, @scure/base@npm:^1.1.9, @scure/base@npm:~1.2.1": + version: 1.2.1 + resolution: "@scure/base@npm:1.2.1" + checksum: 10c0/e61068854370855b89c50c28fa4092ea6780f1e0db64ea94075ab574ebcc964f719a3120dc708db324991f4b3e652d92ebda03fce2bf6a4900ceeacf9c0ff933 languageName: node linkType: hard -"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.3, @scure/base@npm:^1.1.5, @scure/base@npm:^1.1.7, @scure/base@npm:~1.1.6, @scure/base@npm:~1.1.7, @scure/base@npm:~1.1.8": +"@scure/base@npm:~1.1.6, @scure/base@npm:~1.1.7, @scure/base@npm:~1.1.8": version: 1.1.9 resolution: "@scure/base@npm:1.1.9" checksum: 10c0/77a06b9a2db8144d22d9bf198338893d77367c51b58c72b99df990c0a11f7cadd066d4102abb15e3ca6798d1529e3765f55c4355742465e49aed7a0c01fe76e8 @@ -3005,7 +3035,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.5.0, @scure/bip32@npm:^1.5.0": +"@scure/bip32@npm:1.5.0": version: 1.5.0 resolution: "@scure/bip32@npm:1.5.0" dependencies: @@ -3016,6 +3046,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:^1.5.0": + version: 1.6.0 + resolution: "@scure/bip32@npm:1.6.0" + dependencies: + "@noble/curves": "npm:~1.7.0" + "@noble/hashes": "npm:~1.6.0" + "@scure/base": "npm:~1.2.1" + checksum: 10c0/5a5eff8c0bc0b53d70528c5eda6efa7ed6d186a5c9ba0a339edf9c150ee3f331d837ffe29d2c6c6336b1f88ad90aa8b6e596a4950217343f36916d8024f79bdf + languageName: node + linkType: hard + "@scure/bip39@npm:1.3.0": version: 1.3.0 resolution: "@scure/bip39@npm:1.3.0" @@ -3026,7 +3067,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.4.0, @scure/bip39@npm:^1.4.0": +"@scure/bip39@npm:1.4.0": version: 1.4.0 resolution: "@scure/bip39@npm:1.4.0" dependencies: @@ -3036,6 +3077,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:^1.4.0": + version: 1.5.0 + resolution: "@scure/bip39@npm:1.5.0" + dependencies: + "@noble/hashes": "npm:~1.6.0" + "@scure/base": "npm:~1.2.1" + checksum: 10c0/114ab88fb00269d17a73d5c39a2cade47403e05f6df5a8d6f5da6e7f2b071966fe8f656a740dc3399acd006163f234e82b680544c38004703dbb60f8a29daf73 + languageName: node + linkType: hard + "@sec-ant/readable-stream@npm:^0.4.1": version: 0.4.1 resolution: "@sec-ant/readable-stream@npm:0.4.1" @@ -3241,10 +3292,10 @@ __metadata: languageName: node linkType: hard -"@substrate/connect-known-chains@npm:^1.1.5, @substrate/connect-known-chains@npm:^1.7.0": - version: 1.7.0 - resolution: "@substrate/connect-known-chains@npm:1.7.0" - checksum: 10c0/454f115eed8c2299b9ba66c8c3328af80767c501fb15a4e54e6788cc5d9ab6289c91a44a215dbaab8df4fda41063bafff5eee3926b43cd8787af86610e45c673 +"@substrate/connect-known-chains@npm:^1.1.5, @substrate/connect-known-chains@npm:^1.8.0": + version: 1.8.0 + resolution: "@substrate/connect-known-chains@npm:1.8.0" + checksum: 10c0/9315ca8f6a7bfc2d06176c217be68a81109e32da6a90cda8f0827c8101fee6aa696264953c06646c2c450bf3adbae481df0ca1ea1988533c127a03fcec67827c languageName: node linkType: hard @@ -3261,14 +3312,14 @@ __metadata: linkType: hard "@substrate/connect@npm:^2.0.1": - version: 2.0.1 - resolution: "@substrate/connect@npm:2.0.1" + version: 2.1.0 + resolution: "@substrate/connect@npm:2.1.0" dependencies: "@substrate/connect-extension-protocol": "npm:^2.2.1" - "@substrate/connect-known-chains": "npm:^1.7.0" + "@substrate/connect-known-chains": "npm:^1.8.0" "@substrate/smoldot-discovery": "npm:^2.0.1" - smoldot: "npm:2.0.30" - checksum: 10c0/b6e828a980cae0a07d6330fd68bb80f8c337fd1698a1aa19ff5117c5b67c60848678ef4a6a6fde55eb419e9c82c974d98eaa4cfd19834fcd641bf4a38d870ddc + smoldot: "npm:^2.0.34" + checksum: 10c0/2a754d5c553a99e20fecb5f57c74f7a589a0ae6ea70c7a7e19f31d9f546b019d827d32945c21164db9934c17b09f415821e73aca5e11f8c27e0651406c99c836 languageName: node linkType: hard @@ -3439,92 +3490,92 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-darwin-arm64@npm:1.9.2" +"@swc/core-darwin-arm64@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-darwin-arm64@npm:1.9.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-darwin-x64@npm:1.9.2" +"@swc/core-darwin-x64@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-darwin-x64@npm:1.9.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.9.2" +"@swc/core-linux-arm-gnueabihf@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.9.3" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-arm64-gnu@npm:1.9.2" +"@swc/core-linux-arm64-gnu@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm64-gnu@npm:1.9.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-arm64-musl@npm:1.9.2" +"@swc/core-linux-arm64-musl@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-arm64-musl@npm:1.9.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-x64-gnu@npm:1.9.2" +"@swc/core-linux-x64-gnu@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-x64-gnu@npm:1.9.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-linux-x64-musl@npm:1.9.2" +"@swc/core-linux-x64-musl@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-linux-x64-musl@npm:1.9.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-win32-arm64-msvc@npm:1.9.2" +"@swc/core-win32-arm64-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-arm64-msvc@npm:1.9.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-win32-ia32-msvc@npm:1.9.2" +"@swc/core-win32-ia32-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-ia32-msvc@npm:1.9.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.9.2": - version: 1.9.2 - resolution: "@swc/core-win32-x64-msvc@npm:1.9.2" +"@swc/core-win32-x64-msvc@npm:1.9.3": + version: 1.9.3 + resolution: "@swc/core-win32-x64-msvc@npm:1.9.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.7.26": - version: 1.9.2 - resolution: "@swc/core@npm:1.9.2" - dependencies: - "@swc/core-darwin-arm64": "npm:1.9.2" - "@swc/core-darwin-x64": "npm:1.9.2" - "@swc/core-linux-arm-gnueabihf": "npm:1.9.2" - "@swc/core-linux-arm64-gnu": "npm:1.9.2" - "@swc/core-linux-arm64-musl": "npm:1.9.2" - "@swc/core-linux-x64-gnu": "npm:1.9.2" - "@swc/core-linux-x64-musl": "npm:1.9.2" - "@swc/core-win32-arm64-msvc": "npm:1.9.2" - "@swc/core-win32-ia32-msvc": "npm:1.9.2" - "@swc/core-win32-x64-msvc": "npm:1.9.2" + version: 1.9.3 + resolution: "@swc/core@npm:1.9.3" + dependencies: + "@swc/core-darwin-arm64": "npm:1.9.3" + "@swc/core-darwin-x64": "npm:1.9.3" + "@swc/core-linux-arm-gnueabihf": "npm:1.9.3" + "@swc/core-linux-arm64-gnu": "npm:1.9.3" + "@swc/core-linux-arm64-musl": "npm:1.9.3" + "@swc/core-linux-x64-gnu": "npm:1.9.3" + "@swc/core-linux-x64-musl": "npm:1.9.3" + "@swc/core-win32-arm64-msvc": "npm:1.9.3" + "@swc/core-win32-ia32-msvc": "npm:1.9.3" + "@swc/core-win32-x64-msvc": "npm:1.9.3" "@swc/counter": "npm:^0.1.3" - "@swc/types": "npm:^0.1.15" + "@swc/types": "npm:^0.1.17" peerDependencies: "@swc/helpers": "*" dependenciesMeta: @@ -3551,7 +3602,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10c0/697e601fa1246367ca67e87e87c45f6341373ae98d8d24c9586c4069660c73f8675bf94b86cf218308395eda8e355ae076fc8c9c8f7aaa50898c228db38b637d + checksum: 10c0/a9507a5be580518d51cf7f41821a89e1044be6f72930efbdf3877366c27e9ff1dbca3e1a7f18698679f8c345b6698f43cd80d7dfa24ba30dcab493de9b7a336e languageName: node linkType: hard @@ -3562,12 +3613,12 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.15": - version: 0.1.16 - resolution: "@swc/types@npm:0.1.16" +"@swc/types@npm:^0.1.17": + version: 0.1.17 + resolution: "@swc/types@npm:0.1.17" dependencies: "@swc/counter": "npm:^0.1.3" - checksum: 10c0/ca079125ef4c619a4ae908a9d6b99e526e0e21d54443585da9904e9e67a3e7320189843639c6e4435d807dfae5837ab3083a18e86f698e1decf00ecc277ab0ec + checksum: 10c0/29f5c8933a16042956f1adb7383e836ed7646cbf679826e78b53fdd0c08e8572cb42152e527b6b530a9bd1052d33d0972f90f589761ccd252c12652c9b7a72fc languageName: node linkType: hard @@ -3634,12 +3685,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:^22.9.0": - version: 22.9.1 - resolution: "@types/node@npm:22.9.1" +"@types/node@npm:*, @types/node@npm:^22.9.0, @types/node@npm:^22.9.1": + version: 22.10.0 + resolution: "@types/node@npm:22.10.0" dependencies: - undici-types: "npm:~6.19.8" - checksum: 10c0/ea489ae603aa8874e4e88980aab6f2dad09c755da779c88dd142983bfe9609803c89415ca7781f723072934066f63daf2b3339ef084a8ad1a8079cf3958be243 + undici-types: "npm:~6.20.0" + checksum: 10c0/efb3783b6fe74b4300c5bdd4f245f1025887d9b1d0950edae584af58a30d95cc058c10b4b3428f8300e4318468b605240c2ede8fcfb6ead2e0f05bca31e54c1b languageName: node linkType: hard @@ -3830,13 +3881,13 @@ __metadata: linkType: hard "@vitejs/plugin-react-swc@npm:^3.7.0": - version: 3.7.1 - resolution: "@vitejs/plugin-react-swc@npm:3.7.1" + version: 3.7.2 + resolution: "@vitejs/plugin-react-swc@npm:3.7.2" dependencies: "@swc/core": "npm:^1.7.26" peerDependencies: - vite: ^4 || ^5 - checksum: 10c0/2d613e69c0d0b809c94df80ca2b0caf39c50f0b98aa1f8599fd086bc37dac1449898eb6572000e1c133313137cac93440c4cb0861e05820c78bd2c07a52e64a8 + vite: ^4 || ^5 || ^6 + checksum: 10c0/9b9a5e0540791ba96a9fe4e8b8146ab274edcc730315535705f20126d6dfaffe72ae474bac9904ce841976e1959b6ecffd047bb2f0b7abf4d85aae7fbfdd00ab languageName: node linkType: hard @@ -4789,13 +4840,13 @@ __metadata: linkType: hard "axios@npm:^1.7.4": - version: 1.7.7 - resolution: "axios@npm:1.7.7" + version: 1.7.8 + resolution: "axios@npm:1.7.8" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10c0/4499efc89e86b0b49ffddc018798de05fab26e3bf57913818266be73279a6418c3ce8f9e934c7d2d707ab8c095e837fc6c90608fb7715b94d357720b5f568af7 + checksum: 10c0/23ae2d0105aea9170c34ac9b6f30d9b2ab2fa8b1370205d2f7ce98b9f9510ab420148c13359ee837ea5a4bf2fb028ff225bd2fc92052fb0c478c6b4a836e2d5f languageName: node linkType: hard @@ -5015,9 +5066,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001669": - version: 1.0.30001682 - resolution: "caniuse-lite@npm:1.0.30001682" - checksum: 10c0/de9d8bc08e293f8bf6e98e9035055920a75042a655c6abacbf0a4af5ec77882d26df4489721a7f723559c817b13f3fee86b35e827ee2383a5cdf7fb41c60dcd0 + version: 1.0.30001684 + resolution: "caniuse-lite@npm:1.0.30001684" + checksum: 10c0/446485ca3d9caf408a339a44636a86a2b119ec247492393ae661cd93dccd6668401dd2dfec1e149be4e44563cd1e23351b44453a52fa2c2f19e2bf3287c865f6 languageName: node linkType: hard @@ -5376,18 +5427,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3": - version: 7.0.5 - resolution: "cross-spawn@npm:7.0.5" - dependencies: - path-key: "npm:^3.1.0" - shebang-command: "npm:^2.0.0" - which: "npm:^2.0.1" - checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.5": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -5698,9 +5738,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.41": - version: 1.5.63 - resolution: "electron-to-chromium@npm:1.5.63" - checksum: 10c0/fe1b175805309b04e5a2242c3168f22543e5369aed01fceedfe0f0eafe3931e8609d8a140e527394b314cfe64d581913aba6f1d3c72c23069c7d8241e5dfa4ef + version: 1.5.65 + resolution: "electron-to-chromium@npm:1.5.65" + checksum: 10c0/4d2db76ca63d34aad9d5392d850a89fecb4d740a3f0e3ab945f23850ed99789df4e09dd36a28cedcf3b4757dd7c82d5d159bfdf1d29f815d172a9132b4ba3bb9 languageName: node linkType: hard @@ -5834,7 +5874,7 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.1, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3": +"es-abstract@npm:^1.17.5, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5": version: 1.23.5 resolution: "es-abstract@npm:1.23.5" dependencies: @@ -6682,6 +6722,15 @@ __metadata: languageName: node linkType: hard +"figures@npm:^6.1.0": + version: 6.1.0 + resolution: "figures@npm:6.1.0" + dependencies: + is-unicode-supported: "npm:^2.0.0" + checksum: 10c0/9159df4264d62ef447a3931537de92f5012210cf5135c35c010df50a2169377581378149abfe1eb238bd6acbba1c0d547b1f18e0af6eee49e30363cedaffcfe4 + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -6842,6 +6891,13 @@ __metadata: languageName: node linkType: hard +"fs.promises.exists@npm:^1.1.4": + version: 1.1.4 + resolution: "fs.promises.exists@npm:1.1.4" + checksum: 10c0/6e200a97e869c8b84b4dabc5c963d87d20db8704fa12098773b472a61651c0a822b651807ff883e09b8480c41f5184acb65abdd9965b950b3cb2b473b10c3c0f + languageName: node + linkType: hard + "fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -7027,7 +7083,7 @@ __metadata: languageName: node linkType: hard -"globalthis@npm:^1.0.3, globalthis@npm:^1.0.4": +"globalthis@npm:^1.0.4": version: 1.0.4 resolution: "globalthis@npm:1.0.4" dependencies: @@ -7303,8 +7359,8 @@ __metadata: linkType: hard "i18next@npm:^24.0.0": - version: 24.0.0 - resolution: "i18next@npm:24.0.0" + version: 24.0.2 + resolution: "i18next@npm:24.0.2" dependencies: "@babel/runtime": "npm:^7.23.2" peerDependencies: @@ -7312,7 +7368,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/675e87241883dffb18f7bea6d996475777f14406e55c2915ecafb6e4ec28c3e921b675f194e99c3de0f9f234a4a2e204f8c53cfc4fd34a1db89f98c191713034 + checksum: 10c0/6bd2d9a8566026e1a8f97e6d797c323007ad816bf6e90f13cf0114d67908a845b88a5b9701d3d0dabffd38785a6ac145ea9d54265d8ef0f872931c7fa9579e16 languageName: node linkType: hard @@ -7394,6 +7450,13 @@ __metadata: languageName: node linkType: hard +"index-to-position@npm:^0.1.2": + version: 0.1.2 + resolution: "index-to-position@npm:0.1.2" + checksum: 10c0/7c91bde8bafc22684b74a7a24915bee4691cba48352ddb4ebe3b20a3a87bc0fa7a05f586137245ca8f92222a11f341f7631ff7f38cd78a523505d2d02dbfa257 + languageName: node + linkType: hard + "inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" @@ -7577,12 +7640,12 @@ __metadata: languageName: node linkType: hard -"is-finalizationregistry@npm:^1.0.2": - version: 1.0.2 - resolution: "is-finalizationregistry@npm:1.0.2" +"is-finalizationregistry@npm:^1.1.0": + version: 1.1.0 + resolution: "is-finalizationregistry@npm:1.1.0" dependencies: - call-bind: "npm:^1.0.2" - checksum: 10c0/81caecc984d27b1a35c68741156fc651fb1fa5e3e6710d21410abc527eb226d400c0943a167922b2e920f6b3e58b0dede9aa795882b038b85f50b3a4b877db86 + call-bind: "npm:^1.0.7" + checksum: 10c0/1cd94236bfb6e060fe2b973c8726a2782727f7d495b3e8e1d51d3e619c5a3345413706f555956eb5b12af15eba0414118f64a1b19d793ec36b5e6767a13836ac languageName: node linkType: hard @@ -7666,6 +7729,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e + languageName: node + linkType: hard + "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -7893,10 +7963,10 @@ __metadata: languageName: node linkType: hard -"js-tokens@npm:^9.0.0": - version: 9.0.0 - resolution: "js-tokens@npm:9.0.0" - checksum: 10c0/4ad1c12f47b8c8b2a3a99e29ef338c1385c7b7442198a425f3463f3537384dab6032012791bfc2f056ea5ecdb06b1ed4f70e11a3ab3f388d3dcebfe16a52b27d +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e languageName: node linkType: hard @@ -8275,11 +8345,11 @@ __metadata: linkType: hard "magic-string@npm:^0.30.5": - version: 0.30.13 - resolution: "magic-string@npm:0.30.13" + version: 0.30.14 + resolution: "magic-string@npm:0.30.14" dependencies: "@jridgewell/sourcemap-codec": "npm:^1.5.0" - checksum: 10c0/a275faeca1564c545019b4742c38a42ca80226c8c9e0805c32d1a1cc58b0e6ff7bbd914ed885fd10043858a7da0f732cb8f49c8975c3ecebde9cad4b57db5115 + checksum: 10c0/c52c2a6e699dfa8a840e13154da35464a40cd8b07049b695a8b282883b0426c0811af1e36ac26860b4267289340b42772c156a5608e87be97b63d510e617e87a languageName: node linkType: hard @@ -8576,11 +8646,11 @@ __metadata: linkType: hard "nanoid@npm:^3.3.7": - version: 3.3.7 - resolution: "nanoid@npm:3.3.7" + version: 3.3.8 + resolution: "nanoid@npm:3.3.8" bin: nanoid: bin/nanoid.cjs - checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120 languageName: node linkType: hard @@ -9281,24 +9351,24 @@ __metadata: linkType: hard "polkadot-api@npm:^1.7.3": - version: 1.7.6 - resolution: "polkadot-api@npm:1.7.6" + version: 1.7.7 + resolution: "polkadot-api@npm:1.7.7" dependencies: - "@polkadot-api/cli": "npm:0.9.20" - "@polkadot-api/ink-contracts": "npm:0.2.1" + "@polkadot-api/cli": "npm:0.9.21" + "@polkadot-api/ink-contracts": "npm:0.2.2" "@polkadot-api/json-rpc-provider": "npm:0.0.4" - "@polkadot-api/known-chains": "npm:0.5.7" + "@polkadot-api/known-chains": "npm:0.5.8" "@polkadot-api/logs-provider": "npm:0.0.6" - "@polkadot-api/metadata-builders": "npm:0.9.1" - "@polkadot-api/metadata-compatibility": "npm:0.1.11" - "@polkadot-api/observable-client": "npm:0.6.2" - "@polkadot-api/pjs-signer": "npm:0.6.0" + "@polkadot-api/metadata-builders": "npm:0.9.2" + "@polkadot-api/metadata-compatibility": "npm:0.1.12" + "@polkadot-api/observable-client": "npm:0.6.3" + "@polkadot-api/pjs-signer": "npm:0.6.1" "@polkadot-api/polkadot-sdk-compat": "npm:2.3.1" "@polkadot-api/polkadot-signer": "npm:0.1.6" - "@polkadot-api/signer": "npm:0.1.10" + "@polkadot-api/signer": "npm:0.1.11" "@polkadot-api/sm-provider": "npm:0.1.7" "@polkadot-api/smoldot": "npm:0.3.7" - "@polkadot-api/substrate-bindings": "npm:0.9.3" + "@polkadot-api/substrate-bindings": "npm:0.9.4" "@polkadot-api/substrate-client": "npm:0.3.0" "@polkadot-api/utils": "npm:0.1.2" "@polkadot-api/ws-provider": "npm:0.3.6" @@ -9307,7 +9377,7 @@ __metadata: bin: papi: bin/cli.mjs polkadot-api: bin/cli.mjs - checksum: 10c0/caa2df461f2a252aeb8b1b9076bc096a4c6952015a88ac004defc2aac2ea385349767db5394e6a98fef8fdf28eaadbfbcc1914fbacc14d108923f13ed6cf1a12 + checksum: 10c0/c1241d233662b78d9422bb97579bd75f667d15ee1ecc46cb4ef49041b5881d52084c08c97fbb59fee6a3a259a74d61250e0bbe64bb9eb3a1f511bc9fab662b6b languageName: node linkType: hard @@ -9415,9 +9485,9 @@ __metadata: linkType: hard "preact@npm:^10.16.0, preact@npm:^10.24.2": - version: 10.24.3 - resolution: "preact@npm:10.24.3" - checksum: 10c0/c863df6d7be6a660480189762d8a8f2d4148733fc2bb9efbd9d2fd27315d2c7ede850a16077d716c91666c915c0349bd3c9699733e4f08457226a0519f408761 + version: 10.25.0 + resolution: "preact@npm:10.25.0" + checksum: 10c0/703e021e7a268aea929e9c896484608e18dd9f5aa62c71d3205fedf69269d9592e4cfe17adda78ca4cec1f5f0057a062300daeeb477333ba2005a4d9e3fbc970 languageName: node linkType: hard @@ -9730,8 +9800,8 @@ __metadata: linkType: hard "react-i18next@npm:^15.1.1": - version: 15.1.1 - resolution: "react-i18next@npm:15.1.1" + version: 15.1.2 + resolution: "react-i18next@npm:15.1.2" dependencies: "@babel/runtime": "npm:^7.25.0" html-parse-stringify: "npm:^3.0.1" @@ -9743,7 +9813,7 @@ __metadata: optional: true react-native: optional: true - checksum: 10c0/c1e8af21422cdaa1a493b1d73502a496aab84c3318de1137bb56b1f2f37eae4ba5856cf079b0a0abe9f2a18e0457c986ad2e43e70875f7cd43d0efa2f70897a5 + checksum: 10c0/b10052545f0bebcd0b55cf04eb7d58307cdf6645707108bcae28026fe92ce0ef9cf0b35ac9cb940f6fcc22f679c810f2c008e9d8284bd4cfbb26c5649e3885ab languageName: node linkType: hard @@ -9904,18 +9974,18 @@ __metadata: languageName: node linkType: hard -"reflect.getprototypeof@npm:^1.0.4": - version: 1.0.6 - resolution: "reflect.getprototypeof@npm:1.0.6" +"reflect.getprototypeof@npm:^1.0.4, reflect.getprototypeof@npm:^1.0.6": + version: 1.0.7 + resolution: "reflect.getprototypeof@npm:1.0.7" dependencies: call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.1" + es-abstract: "npm:^1.23.5" es-errors: "npm:^1.3.0" get-intrinsic: "npm:^1.2.4" - globalthis: "npm:^1.0.3" - which-builtin-type: "npm:^1.1.3" - checksum: 10c0/baf4ef8ee6ff341600f4720b251cf5a6cb552d6a6ab0fdc036988c451bf16f920e5feb0d46bd4f530a5cce568f1f7aca2d77447ca798920749cfc52783c39b55 + gopd: "npm:^1.0.1" + which-builtin-type: "npm:^1.1.4" + checksum: 10c0/841814f7631b55ee42e198cb14a5c25c0752431ab8f0ad9794c32d46ab9fb0d5ba4939edac1f99a174a21443a1400a72bccbbb9ccd9277e4b4bf6d14aabb31c8 languageName: node linkType: hard @@ -10105,27 +10175,27 @@ __metadata: linkType: hard "rollup@npm:^4.20.0, rollup@npm:^4.24.0": - version: 4.27.3 - resolution: "rollup@npm:4.27.3" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.27.3" - "@rollup/rollup-android-arm64": "npm:4.27.3" - "@rollup/rollup-darwin-arm64": "npm:4.27.3" - "@rollup/rollup-darwin-x64": "npm:4.27.3" - "@rollup/rollup-freebsd-arm64": "npm:4.27.3" - "@rollup/rollup-freebsd-x64": "npm:4.27.3" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.27.3" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.27.3" - "@rollup/rollup-linux-arm64-gnu": "npm:4.27.3" - "@rollup/rollup-linux-arm64-musl": "npm:4.27.3" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.27.3" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.27.3" - "@rollup/rollup-linux-s390x-gnu": "npm:4.27.3" - "@rollup/rollup-linux-x64-gnu": "npm:4.27.3" - "@rollup/rollup-linux-x64-musl": "npm:4.27.3" - "@rollup/rollup-win32-arm64-msvc": "npm:4.27.3" - "@rollup/rollup-win32-ia32-msvc": "npm:4.27.3" - "@rollup/rollup-win32-x64-msvc": "npm:4.27.3" + version: 4.27.4 + resolution: "rollup@npm:4.27.4" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.27.4" + "@rollup/rollup-android-arm64": "npm:4.27.4" + "@rollup/rollup-darwin-arm64": "npm:4.27.4" + "@rollup/rollup-darwin-x64": "npm:4.27.4" + "@rollup/rollup-freebsd-arm64": "npm:4.27.4" + "@rollup/rollup-freebsd-x64": "npm:4.27.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.27.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.27.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.27.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.27.4" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.27.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.27.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.27.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.27.4" + "@rollup/rollup-linux-x64-musl": "npm:4.27.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.27.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.27.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.27.4" "@types/estree": "npm:1.0.6" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -10169,7 +10239,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/789885d3f852ed7ca45bed14194a2ac7a2cf16b6b62b54f691c79e27d5557d31a2d612d3680c26c527a1957e0bd6811806ddd765e0dae589404cf24544ff2838 + checksum: 10c0/1442650cfea5e4617ce14743784f6f578817e31db56f9c8aaf96a82daa9bc20b6ccd66c0d677dbf302a4da3e70664dc3bef11a1aec85e6aff3cecccb945b1d35 languageName: node linkType: hard @@ -10486,21 +10556,21 @@ __metadata: languageName: node linkType: hard -"smoldot@npm:2.0.30": - version: 2.0.30 - resolution: "smoldot@npm:2.0.30" +"smoldot@npm:2.0.33": + version: 2.0.33 + resolution: "smoldot@npm:2.0.33" dependencies: ws: "npm:^8.8.1" - checksum: 10c0/d4aaa7169f2933d3cbcc78720d22a8dee7d038c01f5868d1ccc022e5ebb9deab33ee7fe235f933ae863eccccc65735313c4e2645864730f8a421d44dbd11398b + checksum: 10c0/dada517972924017077b4e8c6f76169ddcd371c270e714db42e034c0c974898a15f640759576b712f9d0aa3e2f1bb01c7e5d2e711e329f0f6837902ac51bb1fa languageName: node linkType: hard -"smoldot@npm:2.0.33": - version: 2.0.33 - resolution: "smoldot@npm:2.0.33" +"smoldot@npm:^2.0.34": + version: 2.0.34 + resolution: "smoldot@npm:2.0.34" dependencies: ws: "npm:^8.8.1" - checksum: 10c0/dada517972924017077b4e8c6f76169ddcd371c270e714db42e034c0c974898a15f640759576b712f9d0aa3e2f1bb01c7e5d2e711e329f0f6837902ac51bb1fa + checksum: 10c0/b9079a70706c54ee16d0ec0b626f6f13807b6130856c570b84ca8dd0506283110017bcc7696d08f3a15e70c9bf2d5ba8a2d7249665347185593193beb1907d1e languageName: node linkType: hard @@ -10903,11 +10973,11 @@ __metadata: linkType: hard "strip-literal@npm:^2.0.0": - version: 2.1.0 - resolution: "strip-literal@npm:2.1.0" + version: 2.1.1 + resolution: "strip-literal@npm:2.1.1" dependencies: - js-tokens: "npm:^9.0.0" - checksum: 10c0/bc8b8c8346125ae3c20fcdaf12e10a498ff85baf6f69597b4ab2b5fbf2e58cfd2827f1a44f83606b852da99a5f6c8279770046ddea974c510c17c98934c9cc24 + js-tokens: "npm:^9.0.1" + checksum: 10c0/66a7353f5ba1ae6a4fb2805b4aba228171847200640083117c41512692e6b2c020e18580402984f55c0ae69c30f857f9a55abd672863e4ca8fdb463fdf93ba19 languageName: node linkType: hard @@ -11039,6 +11109,24 @@ __metadata: languageName: node linkType: hard +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + "thread-stream@npm:^0.15.1": version: 0.15.2 resolution: "thread-stream@npm:0.15.2" @@ -11135,11 +11223,11 @@ __metadata: linkType: hard "ts-api-utils@npm:^1.3.0": - version: 1.4.0 - resolution: "ts-api-utils@npm:1.4.0" + version: 1.4.2 + resolution: "ts-api-utils@npm:1.4.2" peerDependencies: typescript: ">=4.2.0" - checksum: 10c0/1b2bfa50ea52771d564bb143bb69010d25cda03ed573095fbac9b86f717012426443af6647e00e3db70fca60360482a30c1be7cf73c3521c321f6bf5e3594ea0 + checksum: 10c0/b9d82922af42cefa14650397f5ff42a1ff8c8a1b4fac3590fa3e2daeeb3666fbe260a324f55dc748d9653dce30c2a21a148fba928511b2022bedda66423695bf languageName: node linkType: hard @@ -11280,9 +11368,9 @@ __metadata: linkType: hard "type-fest@npm:^4.23.0, type-fest@npm:^4.6.0, type-fest@npm:^4.7.1": - version: 4.27.0 - resolution: "type-fest@npm:4.27.0" - checksum: 10c0/30c8ebe1219725021f5b5006081bd828c4e915de0de65e9a87065f566a96ece706846b2c874ac04c0dcfdfe6b4ae47ad15d3285c9aab162cb79c026dfb2e7556 + version: 4.28.1 + resolution: "type-fest@npm:4.28.1" + checksum: 10c0/8b0dabe2f063dff6dff1964d70f1d844ea13a7534e9aa454b48569d5837f76252bb4518bbd3ca98b4fb65e49a732c555649cf60fc02b0254a0d69eb00c518725 languageName: node linkType: hard @@ -11311,8 +11399,8 @@ __metadata: linkType: hard "typed-array-byte-offset@npm:^1.0.2": - version: 1.0.2 - resolution: "typed-array-byte-offset@npm:1.0.2" + version: 1.0.3 + resolution: "typed-array-byte-offset@npm:1.0.3" dependencies: available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.7" @@ -11320,21 +11408,22 @@ __metadata: gopd: "npm:^1.0.1" has-proto: "npm:^1.0.3" is-typed-array: "npm:^1.1.13" - checksum: 10c0/d2628bc739732072e39269389a758025f75339de2ed40c4f91357023c5512d237f255b633e3106c461ced41907c1bf9a533c7e8578066b0163690ca8bc61b22f + reflect.getprototypeof: "npm:^1.0.6" + checksum: 10c0/5da29585f96671c0521475226d3227000b3e01d1e99208b66bb05b75c7c8f4d0e9cc2e79920f3bfbc792a00102df1daa2608a2753e3f291b671d5a80245bde5b languageName: node linkType: hard "typed-array-length@npm:^1.0.6": - version: 1.0.6 - resolution: "typed-array-length@npm:1.0.6" + version: 1.0.7 + resolution: "typed-array-length@npm:1.0.7" dependencies: call-bind: "npm:^1.0.7" for-each: "npm:^0.3.3" gopd: "npm:^1.0.1" - has-proto: "npm:^1.0.3" is-typed-array: "npm:^1.1.13" possible-typed-array-names: "npm:^1.0.0" - checksum: 10c0/74253d7dc488eb28b6b2711cf31f5a9dcefc9c41b0681fd1c178ed0a1681b4468581a3626d39cd4df7aee3d3927ab62be06aa9ca74e5baf81827f61641445b77 + reflect.getprototypeof: "npm:^1.0.6" + checksum: 10c0/e38f2ae3779584c138a2d8adfa8ecf749f494af3cd3cdafe4e688ce51418c7d2c5c88df1bd6be2bbea099c3f7cea58c02ca02ed438119e91f162a9de23f61295 languageName: node linkType: hard @@ -11354,7 +11443,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.7.2": +"typescript@npm:^5.6.3, typescript@npm:^5.7.2": version: 5.7.2 resolution: "typescript@npm:5.7.2" bin: @@ -11364,7 +11453,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.7.2#optional!builtin": +"typescript@patch:typescript@npm%3A^5.6.3#optional!builtin, typescript@patch:typescript@npm%3A^5.7.2#optional!builtin": version: 5.7.2 resolution: "typescript@patch:typescript@npm%3A5.7.2#optional!builtin::version=5.7.2&hash=5adc0c" bin: @@ -11438,10 +11527,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.8": - version: 6.19.8 - resolution: "undici-types@npm:6.19.8" - checksum: 10c0/078afa5990fba110f6824823ace86073b4638f1d5112ee26e790155f481f2a868cc3e0615505b6f4282bdf74a3d8caad715fd809e870c2bb0704e3ea6082f344 +"undici-types@npm:~6.20.0": + version: 6.20.0 + resolution: "undici-types@npm:6.20.0" + checksum: 10c0/68e659a98898d6a836a9a59e6adf14a5d799707f5ea629433e025ac90d239f75e408e2e5ff086afc3cace26f8b26ee52155293564593fbb4a2f666af57fc59bf languageName: node linkType: hard @@ -11703,8 +11792,8 @@ __metadata: linkType: hard "viem@npm:^2.1.1, viem@npm:^2.21.35": - version: 2.21.48 - resolution: "viem@npm:2.21.48" + version: 2.21.51 + resolution: "viem@npm:2.21.51" dependencies: "@noble/curves": "npm:1.6.0" "@noble/hashes": "npm:1.5.0" @@ -11720,7 +11809,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/e9b2799535263a859bddda25d962b13d2c76aec191e1849dd0f268c32a43eb65932a05cc5be270c92e19d79aafda73884690c0b0fbdb9311266a01ea3f659082 + checksum: 10c0/aca0e6ad3826ce16b7c8463f84063d382769b2fb21d5d1ef3794866e5939e7a73d0e108e0cc45e1d830449d18b935ae1f7d14821adf529586700e066a771cf65 languageName: node linkType: hard @@ -12116,15 +12205,16 @@ __metadata: languageName: node linkType: hard -"which-builtin-type@npm:^1.1.3": - version: 1.1.4 - resolution: "which-builtin-type@npm:1.1.4" +"which-builtin-type@npm:^1.1.4": + version: 1.2.0 + resolution: "which-builtin-type@npm:1.2.0" dependencies: + call-bind: "npm:^1.0.7" function.prototype.name: "npm:^1.1.6" has-tostringtag: "npm:^1.0.2" is-async-function: "npm:^2.0.0" is-date-object: "npm:^1.0.5" - is-finalizationregistry: "npm:^1.0.2" + is-finalizationregistry: "npm:^1.1.0" is-generator-function: "npm:^1.0.10" is-regex: "npm:^1.1.4" is-weakref: "npm:^1.0.2" @@ -12132,7 +12222,7 @@ __metadata: which-boxed-primitive: "npm:^1.0.2" which-collection: "npm:^1.0.2" which-typed-array: "npm:^1.1.15" - checksum: 10c0/a4a76d20d869a81b1dbb4adea31edc7e6c1a4466d3ab7c2cd757c9219d48d3723b04076c85583257b0f0f8e3ebe5af337248b8ceed57b9051cb97bce5bd881d1 + checksum: 10c0/7cd4a8ccfa6a3cb7c2296c716e7266b9f31a66f3e131fe7b185232c16d3ad21442046ec1798c4ec1e19dce7eb99c7751377192e5e734dc07042d14ec0f09b332 languageName: node linkType: hard