From 5896c97f3fb7fee6430b3e6153ffb56e1fdde678 Mon Sep 17 00:00:00 2001 From: tunghp2002 <atsimet@gmail.com> Date: Thu, 19 Dec 2024 09:40:19 +0700 Subject: [PATCH 1/7] [Update] Change priority to get registry to signing --- .../src/koni/background/handlers/Extension.ts | 92 +++++-------------- .../src/koni/background/utils.ts | 69 ++++++++++++++ .../src/services/chain-service/types.ts | 2 + packages/extension-base/src/utils/metadata.ts | 31 +++++-- 4 files changed, 118 insertions(+), 76 deletions(-) create mode 100644 packages/extension-base/src/koni/background/utils.ts diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 7a33f1698c..833e0602e4 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -55,7 +55,6 @@ import { CommonOptimalPath } from '@subwallet/extension-base/types/service-base' import { SwapPair, SwapQuoteResponse, SwapRequest, SwapRequestResult, SwapSubmitParams, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap'; import { _analyzeAddress, BN_ZERO, combineAllAccountProxy, createTransactionFromRLP, isSameAddress, MODULE_SUPPORT, reformatAddress, signatureToHex, Transaction as QrTransaction, transformAccounts, transformAddresses, uniqueStringArray } from '@subwallet/extension-base/utils'; import { parseContractInput, parseEvmRlp } from '@subwallet/extension-base/utils/eth/parseTransaction'; -import { metadataExpand } from '@subwallet/extension-chains'; import { MetadataDef } from '@subwallet/extension-inject/types'; import { getKeypairTypeByAddress, isAddress, isSubstrateAddress, isTonAddress } from '@subwallet/keyring'; import { EthereumKeypairTypes, SubstrateKeypairTypes, TonKeypairTypes } from '@subwallet/keyring/types'; @@ -71,12 +70,13 @@ import { combineLatest, Subject } from 'rxjs'; import { TransactionConfig } from 'web3-core'; import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { Metadata, TypeRegistry } from '@polkadot/types'; -import { ChainProperties } from '@polkadot/types/interfaces'; +import { TypeRegistry } from '@polkadot/types'; import { AnyJson, Registry, SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; import { assert, hexStripPrefix, hexToU8a, isAscii, isHex, u8aToHex } from '@polkadot/util'; import { decodeAddress, isEthereumAddress } from '@polkadot/util-crypto'; +import { getSuitableMetadata, MetadataSource, MetadataWithSource, setupDappRegistry, setupDatabaseRegistry } from '../utils'; + export function isJsonPayload (value: SignerPayloadJSON | SignerPayloadRaw): value is SignerPayloadJSON { return (value as SignerPayloadJSON).genesisHash !== undefined; } @@ -2516,7 +2516,7 @@ export default class KoniExtension { } } - /// Signing substrate request + // Signing substrate request private async signingApprovePasswordV2 ({ id }: RequestSigningApprovePasswordV2): Promise<boolean> { const queued = this.#koniState.getSignRequest(id); @@ -2527,7 +2527,7 @@ export default class KoniExtension { // unlike queued.account.address the following // address is encoded with the default prefix - // which what is used for password caching mapping + // which is used for password caching mapping const { address } = pair; if (!pair) { @@ -2542,77 +2542,33 @@ export default class KoniExtension { const { payload } = request; - let registry: Registry; + let registry: Registry = new TypeRegistry(); if (isJsonPayload(payload)) { const [, chainInfo] = this.#koniState.findNetworkKeyByGenesisHash(payload.genesisHash); - let metadata: MetadataDef | MetadataItem | undefined; - - /** - * Get the metadata for the genesisHash - * @todo: need to handle case metadata store in db - */ - metadata = this.#koniState.knownMetadata.find((meta: MetadataDef) => - meta.genesisHash === payload.genesisHash); + const [apiMetadata, dbMetadata, dappMetadata] = await Promise.all([ + { metadata: this.#koniState.getSubstrateApi(chainInfo?.slug ?? '').metadata, source: MetadataSource.API }, + { metadata: await this.#koniState.chainService.getMetadataByHash(payload.genesisHash), source: MetadataSource.DB }, + { metadata: this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash), source: MetadataSource.DAPP } + ]); - if (metadata) { - // we have metadata, expand it and extract the info/registry - const expanded = metadataExpand(metadata, false); + const allMetadata: MetadataWithSource[] = [ + { metadata: apiMetadata.metadata, source: apiMetadata.source }, + { metadata: dbMetadata.metadata, source: dbMetadata.source }, + { metadata: dappMetadata.metadata, source: dappMetadata.source } + ].filter((item) => item.metadata); - registry = expanded.registry; - registry.setSignedExtensions(payload.signedExtensions, expanded.definition.userExtensions); + if (allMetadata.length === 0) { + registry.setSignedExtensions(payload.signedExtensions); } else { - metadata = await this.#koniState.chainService.getMetadataByHash(payload.genesisHash); - - if (metadata) { - registry = new TypeRegistry(); - - const _metadata = new Metadata(registry, metadata.hexValue); + const { metadata, source } = getSuitableMetadata(allMetadata, parseInt(payload.specVersion)); - registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, - tokenDecimals: chainInfo?.substrateInfo?.decimals, - tokenSymbol: chainInfo?.substrateInfo?.symbol - }) as unknown as ChainProperties); - registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); - } else { - // we have no metadata, create a new registry - registry = new TypeRegistry(); - registry.setSignedExtensions(payload.signedExtensions); - } + registry = source === MetadataSource.API && chainInfo + ? this.#koniState.getSubstrateApi(chainInfo.slug).api.registry as unknown as TypeRegistry + : source === MetadataSource.DB && chainInfo + ? setupDatabaseRegistry(metadata as MetadataItem, chainInfo, payload) + : setupDappRegistry(metadata as MetadataDef, payload); } - - if (!metadata) { - /* - * Some networks must have metadata to signing, - * so if the chain not active (cannot use metadata from api), it must be rejected - * */ - if ( - chainInfo && - (_API_OPTIONS_CHAIN_GROUP.avail.includes(chainInfo.slug) || _API_OPTIONS_CHAIN_GROUP.goldberg.includes(chainInfo.slug)) // The special case for chains that need metadata to signing - ) { - // For case the chain does not have any provider - if (!Object.keys(chainInfo.providers).length) { - reject(new Error('{{chain}} network does not have any provider to connect, please update metadata from dApp'.replaceAll('{{chain}}', chainInfo.name))); - - return false; - } - - const isChainActive = this.#koniState.getChainStateByKey(chainInfo.slug).active; - - if (!isChainActive) { - reject(new Error('Please activate {{chain}} network before signing'.replaceAll('{{chain}}', chainInfo.name))); - - return false; - } - - registry = this.#koniState.getSubstrateApi(chainInfo.slug).api.registry as unknown as TypeRegistry; - } - } - } else { - // for non-payload, just create a registry to use - registry = new TypeRegistry(); } const result = request.sign(registry as unknown as TypeRegistry, pair); diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts new file mode 100644 index 0000000000..0df2d98858 --- /dev/null +++ b/packages/extension-base/src/koni/background/utils.ts @@ -0,0 +1,69 @@ +// Copyright 2019-2022 @subwallet/extension-koni authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { _ChainInfo } from '@subwallet/chain-list/types'; +import { MetadataItem } from '@subwallet/extension-base/background/KoniTypes'; +import { metadataExpand } from '@subwallet/extension-chains/bundle'; +import { MetadataDef } from '@subwallet/extension-inject/types'; + +import { Metadata, TypeRegistry } from '@polkadot/types'; +import { ChainProperties } from '@polkadot/types/interfaces'; +import { SignerPayloadJSON } from '@polkadot/types/types'; + +export enum MetadataSource { + API = 'api', + DB = 'db', + DAPP = 'dapp' +} + +export interface MetadataWithSource{ + metadata: MetadataDef | MetadataItem | undefined; + source: MetadataSource; +} + +export function getSuitableMetadata (metadatas: MetadataWithSource[], payloadSpecVersion: number) { + const sortedMetadatas = metadatas + .filter((meta): meta is MetadataWithSource => meta.metadata !== undefined) + .map((meta) => ({ + meta: meta.metadata, + source: meta.source, + distance: Math.abs(Number(meta.metadata?.specVersion) - payloadSpecVersion), + isHigher: Number(meta.metadata?.specVersion) >= payloadSpecVersion + })) + .sort((a, b) => { + if (a.distance !== b.distance) { + return a.distance - b.distance; + } + + return Number(b.meta?.specVersion) - Number(a.meta?.specVersion); + }); + + return { + metadata: sortedMetadatas[0].meta, + source: sortedMetadatas[0].source + }; +} + +export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo, payload: SignerPayloadJSON) { + const registry = new TypeRegistry(); + const _metadata = new Metadata(registry, metadata.hexValue); + + registry.register(metadata.types); + registry.setChainProperties(registry.createType('ChainProperties', { + ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, + tokenDecimals: chainInfo?.substrateInfo?.decimals, + tokenSymbol: chainInfo?.substrateInfo?.symbol + }) as unknown as ChainProperties); + registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); + + return registry; +} + +export function setupDappRegistry (metadata: MetadataDef, payload: SignerPayloadJSON) { + const expanded = metadataExpand(metadata, false); + const registry = expanded.registry; + + registry.setSignedExtensions(payload.signedExtensions, expanded.definition.userExtensions); + + return registry; +} diff --git a/packages/extension-base/src/services/chain-service/types.ts b/packages/extension-base/src/services/chain-service/types.ts index 5d0b84b84d..6a975af9c6 100644 --- a/packages/extension-base/src/services/chain-service/types.ts +++ b/packages/extension-base/src/services/chain-service/types.ts @@ -6,6 +6,7 @@ import type { ApiInterfaceRx } from '@polkadot/api/types'; import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _CrowdloanFund } from '@subwallet/chain-list/types'; +import { MetadataItem } from '@subwallet/extension-base/background/KoniTypes'; import { AccountState, TxByMsgResponse } from '@subwallet/extension-base/services/balance-service/helpers/subscribe/ton/types'; import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chain-service/handler/types'; import { TonWalletContract } from '@subwallet/keyring/types'; @@ -94,6 +95,7 @@ export interface _SubstrateApiState { } export interface _SubstrateApi extends _SubstrateApiState, _ChainBaseApi, _SubstrateApiAdapter { + metadata?: MetadataItem; api: ApiPromise; isReady: Promise<_SubstrateApi>; connect: (_callbackUpdateMetadata?: (substrateApi: _SubstrateApi) => void) => void; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 41c6c47c2d..6dfd1eadf9 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -4,6 +4,7 @@ import { ChainService } from '@subwallet/extension-base/services/chain-service'; import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types'; +import { ApiPromise } from '@polkadot/api'; import { getSpecExtensions, getSpecTypes } from '@polkadot/types-known'; import { u8aToHex } from '@polkadot/util'; import { HexString } from '@polkadot/util/types'; @@ -25,6 +26,22 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; +const getMetadataV15 = async (api: ApiPromise): Promise<HexString | undefined> => { + try { + if (api.call.metadata.metadataAtVersion) { + const metadataV15 = await api.call.metadata.metadataAtVersion(15); + + if (!metadataV15.isEmpty) { + return metadataV15.unwrap().toHex(); + } + } + } catch (err) { + console.error('Error fetching metadata V15:', err); + } + + return undefined; +}; + export const cacheMetadata = ( chain: string, substrateApi: _SubstrateApi, @@ -42,23 +59,21 @@ export const cacheMetadata = ( return; } - const systemChain = await api.rpc.system.chain(); + const systemChain = api.runtimeChain; // const _metadata: Option<OpaqueMetadata> = await api.call.metadata.metadataAtVersion(15); // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object - let hexV15: HexString | undefined; - - const metadataV15 = await api.call.metadata.metadataAtVersion(15); - if (!metadataV15.isEmpty) { - hexV15 = metadataV15.unwrap().toHex(); - } + const [metadataHex, hexV15] = await Promise.all([ + Promise.resolve(api.runtimeMetadata.toHex()), + getMetadataV15(api) + ]); chainService?.upsertMetadata(chain, { chain: chain, genesisHash: genesisHash, specName: specName, specVersion: currentSpecVersion, - hexValue: api.runtimeMetadata.toHex(), + hexValue: metadataHex, types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record<string, string>, userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), hexV15 From ccb80e45593730f88a44e33af38b2b14866182a6 Mon Sep 17 00:00:00 2001 From: tunghp2002 <atsimet@gmail.com> Date: Mon, 23 Dec 2024 11:11:32 +0700 Subject: [PATCH 2/7] [Update] Refactor priority logic --- .../src/koni/background/handlers/Extension.ts | 14 +--- .../src/koni/background/utils.ts | 73 ++++++++++++++++--- packages/extension-base/src/utils/metadata.ts | 43 ++++++----- 3 files changed, 88 insertions(+), 42 deletions(-) diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 833e0602e4..39d47ee680 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -75,7 +75,7 @@ import { AnyJson, Registry, SignerPayloadJSON, SignerPayloadRaw } from '@polkado import { assert, hexStripPrefix, hexToU8a, isAscii, isHex, u8aToHex } from '@polkadot/util'; import { decodeAddress, isEthereumAddress } from '@polkadot/util-crypto'; -import { getSuitableMetadata, MetadataSource, MetadataWithSource, setupDappRegistry, setupDatabaseRegistry } from '../utils'; +import { getSuitableRegistry, MetadataSource, MetadataWithSource } from '../utils'; export function isJsonPayload (value: SignerPayloadJSON | SignerPayloadRaw): value is SignerPayloadJSON { return (value as SignerPayloadJSON).genesisHash !== undefined; @@ -2546,14 +2546,12 @@ export default class KoniExtension { if (isJsonPayload(payload)) { const [, chainInfo] = this.#koniState.findNetworkKeyByGenesisHash(payload.genesisHash); - const [apiMetadata, dbMetadata, dappMetadata] = await Promise.all([ - { metadata: this.#koniState.getSubstrateApi(chainInfo?.slug ?? '').metadata, source: MetadataSource.API }, + const [dbMetadata, dappMetadata] = await Promise.all([ { metadata: await this.#koniState.chainService.getMetadataByHash(payload.genesisHash), source: MetadataSource.DB }, { metadata: this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash), source: MetadataSource.DAPP } ]); const allMetadata: MetadataWithSource[] = [ - { metadata: apiMetadata.metadata, source: apiMetadata.source }, { metadata: dbMetadata.metadata, source: dbMetadata.source }, { metadata: dappMetadata.metadata, source: dappMetadata.source } ].filter((item) => item.metadata); @@ -2561,13 +2559,7 @@ export default class KoniExtension { if (allMetadata.length === 0) { registry.setSignedExtensions(payload.signedExtensions); } else { - const { metadata, source } = getSuitableMetadata(allMetadata, parseInt(payload.specVersion)); - - registry = source === MetadataSource.API && chainInfo - ? this.#koniState.getSubstrateApi(chainInfo.slug).api.registry as unknown as TypeRegistry - : source === MetadataSource.DB && chainInfo - ? setupDatabaseRegistry(metadata as MetadataItem, chainInfo, payload) - : setupDappRegistry(metadata as MetadataDef, payload); + registry = getSuitableRegistry(allMetadata, payload, chainInfo, this.#koniState); } } diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index 0df2d98858..bd5f447763 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -8,7 +8,9 @@ import { MetadataDef } from '@subwallet/extension-inject/types'; import { Metadata, TypeRegistry } from '@polkadot/types'; import { ChainProperties } from '@polkadot/types/interfaces'; -import { SignerPayloadJSON } from '@polkadot/types/types'; +import { Registry, SignerPayloadJSON } from '@polkadot/types/types'; + +import KoniState from './handlers/State'; export enum MetadataSource { API = 'api', @@ -21,8 +23,49 @@ export interface MetadataWithSource{ source: MetadataSource; } -export function getSuitableMetadata (metadatas: MetadataWithSource[], payloadSpecVersion: number) { - const sortedMetadatas = metadatas +export interface CachedChainProperties { + ss58Format: number; + tokenDecimals: number | undefined; + tokenSymbol: string | undefined; +} + +const cachedChainProperties: Map<string, Promise<CachedChainProperties | null>> = new Map(); + +function getChainProperties (chainInfo: _ChainInfo, genesisHash: string): Promise<CachedChainProperties | null> { + const cachedPromise = cachedChainProperties.get(genesisHash); + + if (cachedPromise) { + return cachedPromise; + } + + const chainPropertiesPromise = new Promise<CachedChainProperties | null>((resolve) => { + const chainProperties: CachedChainProperties = { + ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, + tokenDecimals: chainInfo?.substrateInfo?.decimals, + tokenSymbol: chainInfo?.substrateInfo?.symbol + }; + + cachedChainProperties.set(genesisHash, Promise.resolve(chainProperties)); + resolve(chainProperties); + }); + + cachedChainProperties.set(genesisHash, chainPropertiesPromise); + + return chainPropertiesPromise; +} + +export function getSuitableRegistry (metadatas: MetadataWithSource[], payload: SignerPayloadJSON, chainInfo: _ChainInfo | undefined, koniState: KoniState) { + const api = chainInfo ? koniState.getSubstrateApi(chainInfo.slug).api : undefined; + const apiSpecVersion = api?.runtimeVersion.specVersion.toString(); + const payloadSpecVersion = parseInt(payload.specVersion); + const extendedMetadatas = [{ + metadata: { specVersion: apiSpecVersion }, + source: MetadataSource.API + }, + ...metadatas + ]; + + const sortedMetadatas = extendedMetadatas .filter((meta): meta is MetadataWithSource => meta.metadata !== undefined) .map((meta) => ({ meta: meta.metadata, @@ -38,22 +81,28 @@ export function getSuitableMetadata (metadatas: MetadataWithSource[], payloadSpe return Number(b.meta?.specVersion) - Number(a.meta?.specVersion); }); - return { - metadata: sortedMetadatas[0].meta, - source: sortedMetadatas[0].source - }; + const closestMetadata = sortedMetadatas[0]; + let registry: Registry; + + if (closestMetadata.source === MetadataSource.API) { + registry = api?.registry as unknown as TypeRegistry; + } else if (closestMetadata.source === MetadataSource.DB && chainInfo) { + registry = setupDatabaseRegistry(closestMetadata.meta as MetadataItem, chainInfo, payload); + } else { + registry = setupDappRegistry(closestMetadata.meta as MetadataDef, payload); + } + + return registry; } export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo, payload: SignerPayloadJSON) { const registry = new TypeRegistry(); const _metadata = new Metadata(registry, metadata.hexValue); + const chainProperties = getChainProperties(chainInfo, payload.genesisHash); + registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, - tokenDecimals: chainInfo?.substrateInfo?.decimals, - tokenSymbol: chainInfo?.substrateInfo?.symbol - }) as unknown as ChainProperties); + registry.setChainProperties(registry.createType('ChainProperties', chainProperties) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); return registry; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 6dfd1eadf9..e0df7b96d8 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -26,20 +26,27 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; -const getMetadataV15 = async (api: ApiPromise): Promise<HexString | undefined> => { - try { - if (api.call.metadata.metadataAtVersion) { - const metadataV15 = await api.call.metadata.metadataAtVersion(15); +const storeMetadataV15: Map<string, HexString | undefined> = new Map(); +const getMetadataV15 = (api: ApiPromise) => { + const genesisHash = api.genesisHash.toHex(); + + api.call.metadata.metadataAtVersion(15) + .then((metadataV15) => { if (!metadataV15.isEmpty) { - return metadataV15.unwrap().toHex(); + const hexValue = metadataV15.unwrap().toHex(); + + storeMetadataV15.set(genesisHash, hexValue); + } else { + storeMetadataV15.set(genesisHash, undefined); } - } - } catch (err) { - console.error('Error fetching metadata V15:', err); - } + }) + .catch((err) => { + console.error('Error:', err); + storeMetadataV15.set(genesisHash, undefined); + }); - return undefined; + return storeMetadataV15.get(genesisHash); }; export const cacheMetadata = ( @@ -62,21 +69,19 @@ export const cacheMetadata = ( const systemChain = api.runtimeChain; // const _metadata: Option<OpaqueMetadata> = await api.call.metadata.metadataAtVersion(15); // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object + const metadataHex = api.runtimeMetadata.toHex(); + const metadataV15 = getMetadataV15(api); - const [metadataHex, hexV15] = await Promise.all([ - Promise.resolve(api.runtimeMetadata.toHex()), - getMetadataV15(api) - ]); - - chainService?.upsertMetadata(chain, { + const updateMetadata = { chain: chain, genesisHash: genesisHash, specName: specName, specVersion: currentSpecVersion, hexValue: metadataHex, types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record<string, string>, - userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), - hexV15 - }).catch(console.error); + userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName) + }; + + chainService?.upsertMetadata(chain, { ...updateMetadata, hexV15: metadataV15 }).catch(console.error); }).catch(console.error); }; From 5cbb5b9d2d69799c74f57fbee154ef571af8cfbe Mon Sep 17 00:00:00 2001 From: tunghp2002 <atsimet@gmail.com> Date: Mon, 23 Dec 2024 16:16:22 +0700 Subject: [PATCH 3/7] [Update] Refactor priority logic v2 --- .../src/background/KoniTypes.ts | 3 + .../src/koni/background/handlers/Extension.ts | 28 ++-- .../src/koni/background/utils.ts | 132 ++++++++---------- .../src/services/chain-service/types.ts | 2 - packages/extension-base/src/utils/metadata.ts | 74 +++++----- 5 files changed, 117 insertions(+), 122 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index ff96b76aeb..b297bc6c8a 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -277,6 +277,9 @@ export interface MetadataItem { types: Record<string, Record<string, string> | string>; userExtensions?: ExtDef; hexV15?: HexString; + ss58Format?: number; + tokenDecimals?: number; + tokenSymbol?: string; } export interface CrowdloanItem { diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 39d47ee680..00a9267a25 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -75,7 +75,7 @@ import { AnyJson, Registry, SignerPayloadJSON, SignerPayloadRaw } from '@polkado import { assert, hexStripPrefix, hexToU8a, isAscii, isHex, u8aToHex } from '@polkadot/util'; import { decodeAddress, isEthereumAddress } from '@polkadot/util-crypto'; -import { getSuitableRegistry, MetadataSource, MetadataWithSource } from '../utils'; +import { getSuitableRegistry, RegistrySource, setupApiRegistry, setupDappRegistry, setupDatabaseRegistry } from '../utils'; export function isJsonPayload (value: SignerPayloadJSON | SignerPayloadRaw): value is SignerPayloadJSON { return (value as SignerPayloadJSON).genesisHash !== undefined; @@ -2546,20 +2546,24 @@ export default class KoniExtension { if (isJsonPayload(payload)) { const [, chainInfo] = this.#koniState.findNetworkKeyByGenesisHash(payload.genesisHash); - const [dbMetadata, dappMetadata] = await Promise.all([ - { metadata: await this.#koniState.chainService.getMetadataByHash(payload.genesisHash), source: MetadataSource.DB }, - { metadata: this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash), source: MetadataSource.DAPP } - ]); - - const allMetadata: MetadataWithSource[] = [ - { metadata: dbMetadata.metadata, source: dbMetadata.source }, - { metadata: dappMetadata.metadata, source: dappMetadata.source } - ].filter((item) => item.metadata); - if (allMetadata.length === 0) { + const allRegistry: RegistrySource[] = [ + setupApiRegistry(chainInfo, this.#koniState), + setupDatabaseRegistry( + await this.#koniState.chainService.getMetadataByHash(payload.genesisHash) as MetadataItem, + chainInfo, + payload + ), + setupDappRegistry( + this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash) as MetadataDef, + payload + ) + ].filter((item): item is RegistrySource => item !== null && item.registry !== undefined); + + if (allRegistry.length === 0) { registry.setSignedExtensions(payload.signedExtensions); } else { - registry = getSuitableRegistry(allMetadata, payload, chainInfo, this.#koniState); + registry = getSuitableRegistry(allRegistry, payload).metadata; } } diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index bd5f447763..e4ca8a42ba 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -12,107 +12,89 @@ import { Registry, SignerPayloadJSON } from '@polkadot/types/types'; import KoniState from './handlers/State'; -export enum MetadataSource { - API = 'api', - DB = 'db', - DAPP = 'dapp' +export interface RegistrySource{ + registry: Registry, + specVersion: string | number, } -export interface MetadataWithSource{ - metadata: MetadataDef | MetadataItem | undefined; - source: MetadataSource; -} - -export interface CachedChainProperties { - ss58Format: number; - tokenDecimals: number | undefined; - tokenSymbol: string | undefined; -} - -const cachedChainProperties: Map<string, Promise<CachedChainProperties | null>> = new Map(); - -function getChainProperties (chainInfo: _ChainInfo, genesisHash: string): Promise<CachedChainProperties | null> { - const cachedPromise = cachedChainProperties.get(genesisHash); - - if (cachedPromise) { - return cachedPromise; - } - - const chainPropertiesPromise = new Promise<CachedChainProperties | null>((resolve) => { - const chainProperties: CachedChainProperties = { - ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, - tokenDecimals: chainInfo?.substrateInfo?.decimals, - tokenSymbol: chainInfo?.substrateInfo?.symbol - }; - - cachedChainProperties.set(genesisHash, Promise.resolve(chainProperties)); - resolve(chainProperties); - }); - - cachedChainProperties.set(genesisHash, chainPropertiesPromise); - - return chainPropertiesPromise; -} - -export function getSuitableRegistry (metadatas: MetadataWithSource[], payload: SignerPayloadJSON, chainInfo: _ChainInfo | undefined, koniState: KoniState) { - const api = chainInfo ? koniState.getSubstrateApi(chainInfo.slug).api : undefined; - const apiSpecVersion = api?.runtimeVersion.specVersion.toString(); +export function getSuitableRegistry (registries: RegistrySource[], payload: SignerPayloadJSON) { const payloadSpecVersion = parseInt(payload.specVersion); - const extendedMetadatas = [{ - metadata: { specVersion: apiSpecVersion }, - source: MetadataSource.API - }, - ...metadatas - ]; - - const sortedMetadatas = extendedMetadatas - .filter((meta): meta is MetadataWithSource => meta.metadata !== undefined) - .map((meta) => ({ - meta: meta.metadata, - source: meta.source, - distance: Math.abs(Number(meta.metadata?.specVersion) - payloadSpecVersion), - isHigher: Number(meta.metadata?.specVersion) >= payloadSpecVersion - })) + const sortedRegistries = registries + .filter((registrySource): registrySource is RegistrySource => registrySource.registry !== undefined) + .map((registrySource) => { + const specVersion = Number(registrySource.specVersion); + const distance = Math.abs(specVersion - payloadSpecVersion); + const isHigher = specVersion >= payloadSpecVersion; + + return { + registry: registrySource.registry, + specVersion, + distance, + isHigher + }; + }) .sort((a, b) => { if (a.distance !== b.distance) { return a.distance - b.distance; } - return Number(b.meta?.specVersion) - Number(a.meta?.specVersion); + return a.specVersion - b.specVersion; }); - const closestMetadata = sortedMetadatas[0]; - let registry: Registry; + return { + metadata: sortedRegistries[0].registry + }; +} - if (closestMetadata.source === MetadataSource.API) { - registry = api?.registry as unknown as TypeRegistry; - } else if (closestMetadata.source === MetadataSource.DB && chainInfo) { - registry = setupDatabaseRegistry(closestMetadata.meta as MetadataItem, chainInfo, payload); - } else { - registry = setupDappRegistry(closestMetadata.meta as MetadataDef, payload); +export function setupApiRegistry (chainInfo: _ChainInfo | undefined, koniState: KoniState): RegistrySource | null { + if (!chainInfo) { + return null; } - return registry; + const api = koniState.getSubstrateApi(chainInfo.slug).api; + const apiSpecVersion = api?.runtimeVersion.specVersion.toString(); + const registry = api?.registry as unknown as TypeRegistry; + + return { + registry, + specVersion: apiSpecVersion + }; } -export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo, payload: SignerPayloadJSON) { +export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo | undefined, payload: SignerPayloadJSON): RegistrySource | null { + if (!metadata || !metadata.genesisHash || !chainInfo) { + return null; + } + const registry = new TypeRegistry(); const _metadata = new Metadata(registry, metadata.hexValue); - const chainProperties = getChainProperties(chainInfo, payload.genesisHash); - registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', chainProperties) as unknown as ChainProperties); + registry.setChainProperties(registry.createType('ChainProperties', { + ss58Format: metadata.ss58Format, + tokenDecimals: metadata.tokenDecimals, + tokenSymbol: metadata.tokenSymbol + }) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); - return registry; + return { + registry, + specVersion: metadata.specVersion + }; } -export function setupDappRegistry (metadata: MetadataDef, payload: SignerPayloadJSON) { +export function setupDappRegistry (metadata: MetadataDef, payload: SignerPayloadJSON): RegistrySource | null { + if (!metadata || !metadata.genesisHash) { + return null; + } + const expanded = metadataExpand(metadata, false); const registry = expanded.registry; registry.setSignedExtensions(payload.signedExtensions, expanded.definition.userExtensions); - return registry; + return { + registry, + specVersion: metadata.specVersion + }; } diff --git a/packages/extension-base/src/services/chain-service/types.ts b/packages/extension-base/src/services/chain-service/types.ts index 6a975af9c6..5d0b84b84d 100644 --- a/packages/extension-base/src/services/chain-service/types.ts +++ b/packages/extension-base/src/services/chain-service/types.ts @@ -6,7 +6,6 @@ import type { ApiInterfaceRx } from '@polkadot/api/types'; import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _CrowdloanFund } from '@subwallet/chain-list/types'; -import { MetadataItem } from '@subwallet/extension-base/background/KoniTypes'; import { AccountState, TxByMsgResponse } from '@subwallet/extension-base/services/balance-service/helpers/subscribe/ton/types'; import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chain-service/handler/types'; import { TonWalletContract } from '@subwallet/keyring/types'; @@ -95,7 +94,6 @@ export interface _SubstrateApiState { } export interface _SubstrateApi extends _SubstrateApiState, _ChainBaseApi, _SubstrateApiAdapter { - metadata?: MetadataItem; api: ApiPromise; isReady: Promise<_SubstrateApi>; connect: (_callbackUpdateMetadata?: (substrateApi: _SubstrateApi) => void) => void; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index e0df7b96d8..5e5db113bd 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -26,27 +26,51 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; -const storeMetadataV15: Map<string, HexString | undefined> = new Map(); +const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise<void> => { + try { + if (api.call.metadata.metadataAtVersion) { + const metadataV15 = await api.call.metadata.metadataAtVersion(15); -const getMetadataV15 = (api: ApiPromise) => { - const genesisHash = api.genesisHash.toHex(); - - api.call.metadata.metadataAtVersion(15) - .then((metadataV15) => { if (!metadataV15.isEmpty) { const hexValue = metadataV15.unwrap().toHex(); - storeMetadataV15.set(genesisHash, hexValue); - } else { - storeMetadataV15.set(genesisHash, undefined); + if (chainService) { + const metadata = await chainService.getMetadata(chain); + + if (metadata) { + await chainService.upsertMetadata(chain, { ...metadata, hexV15: hexValue }); + } + } } - }) - .catch((err) => { - console.error('Error:', err); - storeMetadataV15.set(genesisHash, undefined); - }); + } + } catch (err) { + console.error('Error:', err); + } +}; - return storeMetadataV15.get(genesisHash); +const getMetadata = (chain: string, api: ApiPromise, currentSpecVersion: string, genesisHash: HexString, chainService?: ChainService) => { + const specName = api.runtimeVersion.specName.toString(); + + const systemChain = api.runtimeChain; + // const _metadata: Option<OpaqueMetadata> = await api.call.metadata.metadataAtVersion(15); + // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object + const metadataHex = api.runtimeMetadata.toHex(); + const registry = api.registry; + + const updateMetadata = { + chain: chain, + genesisHash: genesisHash, + specName: specName, + specVersion: currentSpecVersion, + hexValue: metadataHex, + types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record<string, string>, + userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), + ss58Format: registry.chainSS58, + tokenDecimals: registry.chainDecimals[0], + tokenSymbol: registry.chainTokens[0] + }; + + chainService?.upsertMetadata(chain, { ...updateMetadata }).catch(console.error); }; export const cacheMetadata = ( @@ -57,7 +81,6 @@ export const cacheMetadata = ( // Update metadata to database with async methods substrateApi.api.isReady.then(async (api) => { const currentSpecVersion = api.runtimeVersion.specVersion.toString(); - const specName = api.runtimeVersion.specName.toString(); const genesisHash = api.genesisHash.toHex(); const metadata = await chainService?.getMetadata(chain); @@ -66,22 +89,7 @@ export const cacheMetadata = ( return; } - const systemChain = api.runtimeChain; - // const _metadata: Option<OpaqueMetadata> = await api.call.metadata.metadataAtVersion(15); - // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object - const metadataHex = api.runtimeMetadata.toHex(); - const metadataV15 = getMetadataV15(api); - - const updateMetadata = { - chain: chain, - genesisHash: genesisHash, - specName: specName, - specVersion: currentSpecVersion, - hexValue: metadataHex, - types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record<string, string>, - userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName) - }; - - chainService?.upsertMetadata(chain, { ...updateMetadata, hexV15: metadataV15 }).catch(console.error); + getMetadata(chain, api, currentSpecVersion, genesisHash, chainService); + await getMetadataV15(chain, api, chainService); }).catch(console.error); }; From 592f8ea716e52a6b438f3942849e39cfbe6cbfe7 Mon Sep 17 00:00:00 2001 From: tunghp2002 <atsimet@gmail.com> Date: Mon, 23 Dec 2024 17:15:38 +0700 Subject: [PATCH 4/7] [Update] Init metadataV15 store --- .../src/background/KoniTypes.ts | 14 +++- .../src/koni/background/handlers/Extension.ts | 2 +- .../src/koni/background/utils.ts | 10 +-- .../src/services/chain-service/index.ts | 10 ++- .../storage-service/DatabaseService.ts | 4 +- .../storage-service/databases/index.ts | 5 +- .../storage-service/db-stores/MetadataV15.ts | 24 ++++++ .../storage-service/db-stores/index.ts | 2 + packages/extension-base/src/utils/metadata.ts | 80 +++++++++++++------ 9 files changed, 114 insertions(+), 37 deletions(-) create mode 100644 packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index b297bc6c8a..2b892a8f47 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -277,9 +277,17 @@ export interface MetadataItem { types: Record<string, Record<string, string> | string>; userExtensions?: ExtDef; hexV15?: HexString; - ss58Format?: number; - tokenDecimals?: number; - tokenSymbol?: string; + tokenInfo?: { + ss58Format: number; + tokenDecimals: number; + tokenSymbol: string; + }; +} + +export interface MetadataV15Item { + genesisHash: string; + specVersion: string; + hexV15?: HexString; } export interface CrowdloanItem { diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 00a9267a25..647f0fcefe 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -2563,7 +2563,7 @@ export default class KoniExtension { if (allRegistry.length === 0) { registry.setSignedExtensions(payload.signedExtensions); } else { - registry = getSuitableRegistry(allRegistry, payload).metadata; + registry = getSuitableRegistry(allRegistry, payload); } } diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index e4ca8a42ba..d435b987bb 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -41,9 +41,7 @@ export function getSuitableRegistry (registries: RegistrySource[], payload: Sign return a.specVersion - b.specVersion; }); - return { - metadata: sortedRegistries[0].registry - }; + return sortedRegistries[0].registry; } export function setupApiRegistry (chainInfo: _ChainInfo | undefined, koniState: KoniState): RegistrySource | null { @@ -71,9 +69,9 @@ export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _Chain registry.register(metadata.types); registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: metadata.ss58Format, - tokenDecimals: metadata.tokenDecimals, - tokenSymbol: metadata.tokenSymbol + ss58Format: metadata.tokenInfo?.ss58Format, + tokenDecimals: metadata.tokenInfo?.tokenDecimals, + tokenSymbol: metadata.tokenInfo?.tokenSymbol }) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); diff --git a/packages/extension-base/src/services/chain-service/index.ts b/packages/extension-base/src/services/chain-service/index.ts index 5d5e3f7562..424dbd84c8 100644 --- a/packages/extension-base/src/services/chain-service/index.ts +++ b/packages/extension-base/src/services/chain-service/index.ts @@ -13,7 +13,7 @@ import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chai import { _ChainApiStatus, _ChainConnectionStatus, _ChainState, _CUSTOM_PREFIX, _DataMap, _EvmApi, _NetworkUpsertParams, _NFT_CONTRACT_STANDARDS, _SMART_CONTRACT_STANDARDS, _SmartContractTokenInfo, _SubstrateApi, _ValidateCustomAssetRequest, _ValidateCustomAssetResponse } from '@subwallet/extension-base/services/chain-service/types'; import { _isAssetAutoEnable, _isAssetCanPayTxFee, _isAssetFungibleToken, _isChainEnabled, _isCustomAsset, _isCustomChain, _isCustomProvider, _isEqualContractAddress, _isEqualSmartContractAsset, _isLocalToken, _isMantaZkAsset, _isPureEvmChain, _isPureSubstrateChain, _parseAssetRefKey, randomizeProvider, updateLatestChainInfo } from '@subwallet/extension-base/services/chain-service/utils'; import { EventService } from '@subwallet/extension-base/services/event-service'; -import { IChain, IMetadataItem } from '@subwallet/extension-base/services/storage-service/databases'; +import { IChain, IMetadataItem, IMetadataV15Item } from '@subwallet/extension-base/services/storage-service/databases'; import DatabaseService from '@subwallet/extension-base/services/storage-service/DatabaseService'; import AssetSettingStore from '@subwallet/extension-base/stores/AssetSetting'; import { addLazy, calculateMetadataHash, fetchStaticData, filterAssetsByChainAndType, getShortMetadata, MODULE_SUPPORT } from '@subwallet/extension-base/utils'; @@ -2070,6 +2070,14 @@ export class ChainService { return this.dbService.stores.metadata.upsertMetadata(chain, metadata); } + getMetadataV15 (chain: string) { + return this.dbService.stores.metadataV15.getMetadata(chain); + } + + upsertMetadataV15 (chain: string, metadata: IMetadataV15Item) { + return this.dbService.stores.metadataV15.upsertMetadata(chain, metadata); + } + getMetadataByHash (hash: string) { return this.dbService.stores.metadata.getMetadataByGenesisHash(hash); } diff --git a/packages/extension-base/src/services/storage-service/DatabaseService.ts b/packages/extension-base/src/services/storage-service/DatabaseService.ts index b0db514b1f..a91949bf05 100644 --- a/packages/extension-base/src/services/storage-service/DatabaseService.ts +++ b/packages/extension-base/src/services/storage-service/DatabaseService.ts @@ -6,7 +6,7 @@ import { APIItemState, ChainStakingMetadata, CrowdloanItem, MantaPayConfig, NftC import { EventService } from '@subwallet/extension-base/services/event-service'; import { _NotificationInfo } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; import KoniDatabase, { IBalance, ICampaign, IChain, ICrowdloanItem, INft } from '@subwallet/extension-base/services/storage-service/databases'; -import { AssetStore, BalanceStore, ChainStore, CrowdloanStore, MetadataStore, MigrationStore, NftCollectionStore, NftStore, PriceStore, StakingStore, TransactionStore } from '@subwallet/extension-base/services/storage-service/db-stores'; +import { AssetStore, BalanceStore, ChainStore, CrowdloanStore, MetadataStore, MetadataV15Store, MigrationStore, NftCollectionStore, NftStore, PriceStore, StakingStore, TransactionStore } from '@subwallet/extension-base/services/storage-service/db-stores'; import BaseStore from '@subwallet/extension-base/services/storage-service/db-stores/BaseStore'; import CampaignStore from '@subwallet/extension-base/services/storage-service/db-stores/Campaign'; import ChainStakingMetadataStore from '@subwallet/extension-base/services/storage-service/db-stores/ChainStakingMetadata'; @@ -55,6 +55,8 @@ export default class DatabaseService { migration: new MigrationStore(this._db.migrations), metadata: new MetadataStore(this._db.metadata), + metadataV15: new MetadataV15Store(this._db.metadataV15), + chain: new ChainStore(this._db.chain), asset: new AssetStore(this._db.asset), diff --git a/packages/extension-base/src/services/storage-service/databases/index.ts b/packages/extension-base/src/services/storage-service/databases/index.ts index 1cda2f9af1..9f4c525870 100644 --- a/packages/extension-base/src/services/storage-service/databases/index.ts +++ b/packages/extension-base/src/services/storage-service/databases/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { _AssetRef, _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types'; -import { CampaignData, ChainStakingMetadata, CrowdloanItem, MetadataItem, NftCollection, NftItem, NominatorMetadata, PriceJson, StakingItem, TransactionHistoryItem } from '@subwallet/extension-base/background/KoniTypes'; +import { CampaignData, ChainStakingMetadata, CrowdloanItem, MetadataItem, MetadataV15Item, NftCollection, NftItem, NominatorMetadata, PriceJson, StakingItem, TransactionHistoryItem } from '@subwallet/extension-base/background/KoniTypes'; import { _NotificationInfo } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; import { BalanceItem, YieldPoolInfo, YieldPositionInfo } from '@subwallet/extension-base/types'; import Dexie, { Table, Transaction } from 'dexie'; @@ -41,6 +41,7 @@ export interface IMigration { } export interface IMetadataItem extends MetadataItem, DefaultChainDoc {} +export interface IMetadataV15Item extends MetadataV15Item, DefaultChainDoc {} export type IMantaPayLedger = any; @@ -62,6 +63,8 @@ export default class KoniDatabase extends Dexie { public migrations!: Table<IMigration, object>; public metadata!: Table<IMetadataItem, object>; + public metadataV15!: Table<IMetadataV15Item, object>; + public chain!: Table<IChain, object>; public asset!: Table<_ChainAsset, object>; diff --git a/packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts b/packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts new file mode 100644 index 0000000000..a985c2927e --- /dev/null +++ b/packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts @@ -0,0 +1,24 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import BaseStoreWithChain from '@subwallet/extension-base/services/storage-service/db-stores/BaseStoreWithChain'; + +import { IMetadataV15Item } from '../databases'; + +export default class MetadataV15Store extends BaseStoreWithChain<IMetadataV15Item> { + getMetadata (chain: string) { + return this.table.where('chain').equals(chain).first(); + } + + upsertMetadata (chain: string, metadata: IMetadataV15Item) { + return this.table.put(metadata, chain); + } + + getMetadataByGenesisHash (genesisHash: string) { + return this.table.get(genesisHash); + } + + updateMetadataByGenesisHash (genesisHash: string, metadata: IMetadataV15Item) { + return this.table.put(metadata, genesisHash); + } +} diff --git a/packages/extension-base/src/services/storage-service/db-stores/index.ts b/packages/extension-base/src/services/storage-service/db-stores/index.ts index f26e20b498..d3e465eaaf 100644 --- a/packages/extension-base/src/services/storage-service/db-stores/index.ts +++ b/packages/extension-base/src/services/storage-service/db-stores/index.ts @@ -11,5 +11,7 @@ export { default as TransactionStore } from './Transaction'; export { default as MigrationStore } from './Migration'; export { default as MetadataStore } from './Metadata'; +export { default as MetadataV15Store } from './MetadataV15'; + export { default as ChainStore } from './Chain'; export { default as AssetStore } from './Asset'; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 5e5db113bd..01653171a8 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -5,11 +5,26 @@ import { ChainService } from '@subwallet/extension-base/services/chain-service'; import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types'; import { ApiPromise } from '@polkadot/api'; +import { TypeRegistry } from '@polkadot/types'; import { getSpecExtensions, getSpecTypes } from '@polkadot/types-known'; -import { u8aToHex } from '@polkadot/util'; +import { formatBalance, isNumber, u8aToHex } from '@polkadot/util'; import { HexString } from '@polkadot/util/types'; +import { defaults as addressDefaults } from '@polkadot/util-crypto/address/defaults'; import { ExtraInfo, merkleizeMetadata } from '@polkadot-api/merkleize-metadata'; +interface Statics { + api: ApiPromise; + registry: TypeRegistry; +} + +export const statics = { + api: undefined, + registry: new TypeRegistry() +} as unknown as Statics; + +export const DEFAULT_DECIMALS = statics.registry.createType('u32', 12); +export const DEFAULT_SS58 = statics.registry.createType('u32', addressDefaults.prefix); + export const _isRuntimeUpdated = (signedExtensions?: string[]): boolean => { return signedExtensions ? signedExtensions.includes('CheckMetadataHash') : false; }; @@ -28,19 +43,29 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise<void> => { try { + const currentSpecVersion = api.runtimeVersion.specVersion.toString(); + const genesisHash = api.genesisHash.toHex(); + const metadata = await chainService?.getMetadataV15(chain); + + // Avoid date existed metadata + if (metadata && metadata.specVersion === currentSpecVersion && metadata.genesisHash === genesisHash) { + return; + } + if (api.call.metadata.metadataAtVersion) { const metadataV15 = await api.call.metadata.metadataAtVersion(15); if (!metadataV15.isEmpty) { - const hexValue = metadataV15.unwrap().toHex(); + const hexV15 = metadataV15.unwrap().toHex(); + const updateMetadata = { + chain: chain, + genesisHash: genesisHash, + specVersion: currentSpecVersion, + hexV15 - if (chainService) { - const metadata = await chainService.getMetadata(chain); + }; - if (metadata) { - await chainService.upsertMetadata(chain, { ...metadata, hexV15: hexValue }); - } - } + chainService?.upsertMetadataV15(chain, { ...updateMetadata }).catch(console.error); } } } catch (err) { @@ -48,15 +73,33 @@ const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: Cha } }; -const getMetadata = (chain: string, api: ApiPromise, currentSpecVersion: string, genesisHash: HexString, chainService?: ChainService) => { +const getMetadata = async ( + chain: string, + api: ApiPromise, + chainService?: ChainService +) => { + const currentSpecVersion = api.runtimeVersion.specVersion.toString(); + const genesisHash = api.genesisHash.toHex(); const specName = api.runtimeVersion.specName.toString(); + const metadata = await chainService?.getMetadata(chain); + + // Avoid date existed metadata + if (metadata && metadata.specVersion === currentSpecVersion && metadata.genesisHash === genesisHash) { + return; + } const systemChain = api.runtimeChain; - // const _metadata: Option<OpaqueMetadata> = await api.call.metadata.metadataAtVersion(15); - // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object const metadataHex = api.runtimeMetadata.toHex(); const registry = api.registry; + const tokenInfo = { + ss58Format: isNumber(registry.chainSS58) + ? registry.chainSS58 + : DEFAULT_SS58.toNumber(), + tokenDecimals: (registry.chainDecimals || [DEFAULT_DECIMALS.toNumber()])[0], + tokenSymbol: (registry.chainTokens || formatBalance.getDefaults().unit)[0] + }; + const updateMetadata = { chain: chain, genesisHash: genesisHash, @@ -65,9 +108,7 @@ const getMetadata = (chain: string, api: ApiPromise, currentSpecVersion: string, hexValue: metadataHex, types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record<string, string>, userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), - ss58Format: registry.chainSS58, - tokenDecimals: registry.chainDecimals[0], - tokenSymbol: registry.chainTokens[0] + tokenInfo }; chainService?.upsertMetadata(chain, { ...updateMetadata }).catch(console.error); @@ -80,16 +121,7 @@ export const cacheMetadata = ( ): void => { // Update metadata to database with async methods substrateApi.api.isReady.then(async (api) => { - const currentSpecVersion = api.runtimeVersion.specVersion.toString(); - const genesisHash = api.genesisHash.toHex(); - const metadata = await chainService?.getMetadata(chain); - - // Avoid date existed metadata - if (metadata && metadata.specVersion === currentSpecVersion && metadata.genesisHash === genesisHash) { - return; - } - - getMetadata(chain, api, currentSpecVersion, genesisHash, chainService); + await getMetadata(chain, api, chainService); await getMetadataV15(chain, api, chainService); }).catch(console.error); }; From 070518e478abc69b2a3dfd1304b82563e2884dcb Mon Sep 17 00:00:00 2001 From: tunghp2002 <atsimet@gmail.com> Date: Tue, 24 Dec 2024 09:35:34 +0700 Subject: [PATCH 5/7] [Update] Refactor code --- .../src/koni/background/utils.ts | 8 ++---- .../src/services/chain-service/index.ts | 26 ++++++++++--------- packages/extension-base/src/utils/metadata.ts | 11 ++++---- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index d435b987bb..3ad32601d2 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -38,7 +38,7 @@ export function getSuitableRegistry (registries: RegistrySource[], payload: Sign return a.distance - b.distance; } - return a.specVersion - b.specVersion; + return b.specVersion - a.specVersion; }); return sortedRegistries[0].registry; @@ -68,11 +68,7 @@ export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _Chain const _metadata = new Metadata(registry, metadata.hexValue); registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: metadata.tokenInfo?.ss58Format, - tokenDecimals: metadata.tokenInfo?.tokenDecimals, - tokenSymbol: metadata.tokenInfo?.tokenSymbol - }) as unknown as ChainProperties); + registry.setChainProperties(registry.createType('ChainProperties', metadata.tokenInfo) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); return { diff --git a/packages/extension-base/src/services/chain-service/index.ts b/packages/extension-base/src/services/chain-service/index.ts index 424dbd84c8..accbad33e5 100644 --- a/packages/extension-base/src/services/chain-service/index.ts +++ b/packages/extension-base/src/services/chain-service/index.ts @@ -3,7 +3,7 @@ import { AssetLogoMap, AssetRefMap, ChainAssetMap, ChainInfoMap, ChainLogoMap, MultiChainAssetMap } from '@subwallet/chain-list'; import { _AssetRef, _AssetRefPath, _AssetType, _ChainAsset, _ChainInfo, _ChainStatus, _EvmInfo, _MultiChainAsset, _SubstrateChainType, _SubstrateInfo, _TonInfo } from '@subwallet/chain-list/types'; -import { AssetSetting, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; +import { AssetSetting, MetadataItem, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; import { _DEFAULT_ACTIVE_CHAINS, _ZK_ASSET_PREFIX, LATEST_CHAIN_DATA_FETCHING_INTERVAL } from '@subwallet/extension-base/services/chain-service/constants'; import { EvmChainHandler } from '@subwallet/extension-base/services/chain-service/handler/EvmChainHandler'; import { MantaPrivateHandler } from '@subwallet/extension-base/services/chain-service/handler/manta/MantaPrivateHandler'; @@ -2082,42 +2082,44 @@ export class ChainService { return this.dbService.stores.metadata.getMetadataByGenesisHash(hash); } - getExtraInfo (chain: string): Omit<ExtraInfo, 'specVersion' | 'specName'> { - const chainInfo = this.getChainInfoByKey(chain); + getExtraInfo (metadata: MetadataItem): Omit<ExtraInfo, 'specVersion' | 'specName'> { + const tokenInfo = metadata.tokenInfo; return { - decimals: chainInfo.substrateInfo?.decimals ?? 0, - tokenSymbol: chainInfo.substrateInfo?.symbol ?? 'Unit', - base58Prefix: chainInfo.substrateInfo?.addressPrefix ?? 42 + decimals: tokenInfo?.tokenDecimals ?? 0, + tokenSymbol: tokenInfo?.tokenSymbol ?? 'Unit', + base58Prefix: tokenInfo?.ss58Format ?? 42 }; } async calculateMetadataHash (chain: string): Promise<string | undefined> { const metadata = await this.getMetadata(chain); + const metadataV15 = await this.getMetadataV15(chain); - if (!metadata || !metadata.hexV15) { + if (!metadata || !metadataV15 || !metadataV15.hexV15) { return undefined; } - const extraInfo = this.getExtraInfo(chain); + const extraInfo = this.getExtraInfo(metadata); const specVersion = parseInt(metadata.specVersion); const specName = metadata.specName; - const hexV15 = metadata.hexV15; + const hexV15 = metadataV15.hexV15; return calculateMetadataHash({ ...extraInfo, specVersion, specName }, hexV15); } async shortenMetadata (chain: string, txBlob: string): Promise<string | undefined> { const metadata = await this.getMetadata(chain); + const metadataV15 = await this.getMetadataV15(chain); - if (!metadata || !metadata.hexV15) { + if (!metadata || !metadataV15 || !metadataV15.hexV15) { return undefined; } - const extraInfo = this.getExtraInfo(chain); + const extraInfo = this.getExtraInfo(metadata); const specVersion = parseInt(metadata.specVersion); const specName = metadata.specName; - const hexV15 = metadata.hexV15; + const hexV15 = metadataV15.hexV15; return getShortMetadata(txBlob as HexString, { ...extraInfo, specVersion, specName }, hexV15); } diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 01653171a8..8997baa64e 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -41,7 +41,7 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; -const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise<void> => { +const updateMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise<void> => { try { const currentSpecVersion = api.runtimeVersion.specVersion.toString(); const genesisHash = api.genesisHash.toHex(); @@ -62,7 +62,6 @@ const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: Cha genesisHash: genesisHash, specVersion: currentSpecVersion, hexV15 - }; chainService?.upsertMetadataV15(chain, { ...updateMetadata }).catch(console.error); @@ -73,7 +72,7 @@ const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: Cha } }; -const getMetadata = async ( +const updateMetadata = async ( chain: string, api: ApiPromise, chainService?: ChainService @@ -120,8 +119,8 @@ export const cacheMetadata = ( chainService?: ChainService ): void => { // Update metadata to database with async methods - substrateApi.api.isReady.then(async (api) => { - await getMetadata(chain, api, chainService); - await getMetadataV15(chain, api, chainService); + substrateApi.api.isReady.then((api) => { + return updateMetadata(chain, api, chainService) + .then(() => updateMetadataV15(chain, api, chainService)); }).catch(console.error); }; From e112761aa5b3f19ceb8fd048e416ea9452481fa8 Mon Sep 17 00:00:00 2001 From: tunghp2002 <atsimet@gmail.com> Date: Tue, 24 Dec 2024 11:32:50 +0700 Subject: [PATCH 6/7] [Update] Add metadata conditionalVersion --- .../src/services/storage-service/databases/index.ts | 4 ++++ packages/extension-base/src/utils/metadata.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/extension-base/src/services/storage-service/databases/index.ts b/packages/extension-base/src/services/storage-service/databases/index.ts index 9f4c525870..0b21de49fc 100644 --- a/packages/extension-base/src/services/storage-service/databases/index.ts +++ b/packages/extension-base/src/services/storage-service/databases/index.ts @@ -129,6 +129,10 @@ export default class KoniDatabase extends Dexie { this.conditionalVersion(7, { inappNotification: 'id, address, proxyId, [proxyId+actionType], actionType' }); + + this.conditionalVersion(8, { + metadataV15: 'genesisHash, chain' + }); } private conditionalVersion ( diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 8997baa64e..05e5555936 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -120,7 +120,7 @@ export const cacheMetadata = ( ): void => { // Update metadata to database with async methods substrateApi.api.isReady.then((api) => { - return updateMetadata(chain, api, chainService) - .then(() => updateMetadataV15(chain, api, chainService)); + updateMetadata(chain, api, chainService).catch(console.error); + updateMetadataV15(chain, api, chainService).catch(console.error); }).catch(console.error); }; From f1943f06b6d563a577c0baec658462c31016f550 Mon Sep 17 00:00:00 2001 From: tunghp2002 <atsimet@gmail.com> Date: Tue, 24 Dec 2024 11:46:28 +0700 Subject: [PATCH 7/7] [Update] upversion clearmetadataDatabase --- .../src/services/migration-service/scripts/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension-base/src/services/migration-service/scripts/index.ts b/packages/extension-base/src/services/migration-service/scripts/index.ts index 03be614d84..301b328477 100644 --- a/packages/extension-base/src/services/migration-service/scripts/index.ts +++ b/packages/extension-base/src/services/migration-service/scripts/index.ts @@ -62,9 +62,9 @@ export default <Record<string, typeof BaseMigrationJob>>{ '1.2.28-02': MigrateTransactionHistoryBySymbol, '1.2.69-01': MigrateRemoveGenesisHash, '1.2.13-01': ReloadMetadata, - '1.2.14-01': ClearMetadataDatabase, '1.2.32-01': MigratePairData, - '1.3.6-01': MigrateTransactionHistoryBridge + '1.3.6-01': MigrateTransactionHistoryBridge, + '1.3.10-01': ClearMetadataDatabase // [`${EVERYTIME}-1.1.42-02`]: MigrateTransactionHistoryBySymbol // [`${EVERYTIME}-1`]: AutoEnableChainsTokens };