@dedot/api 0.8.1-next.c741884a.1 → 0.8.2-next.6cfafabd.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/client/BaseSubstrateClient.js +61 -1
- package/cjs/executor/StorageQueryExecutor.js +8 -15
- package/cjs/executor/v2/StorageQueryExecutorV2.js +7 -33
- package/cjs/extrinsic/extensions/ExtraSignedExtension.js +24 -5
- package/cjs/extrinsic/extensions/FallbackSignedExtension.js +63 -0
- package/cjs/extrinsic/extensions/index.js +1 -0
- package/cjs/extrinsic/extensions/known/index.js +11 -8
- package/cjs/storage/BaseStorageQuery.js +27 -0
- package/cjs/storage/LegacyStorageQuery.js +67 -0
- package/cjs/storage/NewStorageQuery.js +85 -0
- package/cjs/storage/index.js +3 -0
- package/client/BaseSubstrateClient.d.ts +36 -1
- package/client/BaseSubstrateClient.js +61 -1
- package/client/DedotClient.js +1 -1
- package/executor/StorageQueryExecutor.d.ts +3 -2
- package/executor/StorageQueryExecutor.js +7 -14
- package/executor/v2/StorageQueryExecutorV2.d.ts +4 -6
- package/executor/v2/StorageQueryExecutorV2.js +7 -33
- package/extrinsic/extensions/ExtraSignedExtension.d.ts +6 -0
- package/extrinsic/extensions/ExtraSignedExtension.js +25 -6
- package/extrinsic/extensions/FallbackSignedExtension.d.ts +33 -0
- package/extrinsic/extensions/FallbackSignedExtension.js +58 -0
- package/extrinsic/extensions/index.d.ts +1 -0
- package/extrinsic/extensions/index.js +1 -0
- package/extrinsic/extensions/known/index.d.ts +11 -0
- package/extrinsic/extensions/known/index.js +11 -8
- package/package.json +10 -10
- package/storage/BaseStorageQuery.d.ts +41 -0
- package/storage/BaseStorageQuery.js +23 -0
- package/storage/LegacyStorageQuery.d.ts +35 -0
- package/storage/LegacyStorageQuery.js +63 -0
- package/storage/NewStorageQuery.d.ts +35 -0
- package/storage/NewStorageQuery.js +81 -0
- package/storage/index.d.ts +3 -0
- package/storage/index.js +3 -0
- package/types.d.ts +17 -1
- package/cjs/extrinsic/extensions/known/CheckNonZeroSender.js +0 -10
- package/cjs/extrinsic/extensions/known/CheckWeight.js +0 -10
- package/cjs/extrinsic/extensions/known/PrevalidateAttests.js +0 -11
- package/cjs/extrinsic/extensions/known/StorageWeightReclaim.js +0 -10
- package/extrinsic/extensions/known/CheckNonZeroSender.d.ts +0 -6
- package/extrinsic/extensions/known/CheckNonZeroSender.js +0 -6
- package/extrinsic/extensions/known/CheckWeight.d.ts +0 -6
- package/extrinsic/extensions/known/CheckWeight.js +0 -6
- package/extrinsic/extensions/known/PrevalidateAttests.d.ts +0 -7
- package/extrinsic/extensions/known/PrevalidateAttests.js +0 -7
- package/extrinsic/extensions/known/StorageWeightReclaim.d.ts +0 -6
- package/extrinsic/extensions/known/StorageWeightReclaim.js +0 -6
|
@@ -4,6 +4,7 @@ import { calcRuntimeApiHash, deferred, ensurePresence as _ensurePresence, u8aToH
|
|
|
4
4
|
import { ConstantExecutor, ErrorExecutor, EventExecutor } from '../executor/index.js';
|
|
5
5
|
import { isJsonRpcProvider, JsonRpcClient } from '../json-rpc/index.js';
|
|
6
6
|
import { newProxyChain } from '../proxychain.js';
|
|
7
|
+
import { LegacyStorageQuery, NewStorageQuery, QueryableStorage } from '../storage/index.js';
|
|
7
8
|
const SUPPORTED_METADATA_VERSIONS = [15, 14];
|
|
8
9
|
const MetadataApiHash = calcRuntimeApiHash('Metadata'); // 0x37e397fc7c91f5e4
|
|
9
10
|
const MESSAGE = 'Make sure to call `.connect()` method first before using the API interfaces.';
|
|
@@ -83,13 +84,44 @@ export class BaseSubstrateClient extends JsonRpcClient {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
if (shouldUpdateCache && this._localCache) {
|
|
86
|
-
|
|
87
|
+
const encodedMetadata = u8aToHex($Metadata.tryEncode(metadata));
|
|
88
|
+
await this.safeSetMetadataToCache(metadataKey, encodedMetadata);
|
|
87
89
|
}
|
|
88
90
|
if (!metadata) {
|
|
89
91
|
throw new Error('Cannot load metadata');
|
|
90
92
|
}
|
|
91
93
|
this.setMetadata(metadata);
|
|
92
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Safely set metadata to cache with fallback cleanup if storage limit is exceeded
|
|
97
|
+
*/
|
|
98
|
+
async safeSetMetadataToCache(key, value) {
|
|
99
|
+
if (!this._localCache)
|
|
100
|
+
return;
|
|
101
|
+
try {
|
|
102
|
+
// First attempt to set the metadata
|
|
103
|
+
await this._localCache.set(key, value);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.warn('Failed to store metadata in cache, attempting to clean up old entries:', error);
|
|
107
|
+
try {
|
|
108
|
+
// Get all keys that start with RAW_META/
|
|
109
|
+
const allKeys = await this._localCache.keys();
|
|
110
|
+
const metadataKeys = allKeys.filter((k) => k.startsWith('RAW_META/') && k !== key);
|
|
111
|
+
// Remove all other metadata entries
|
|
112
|
+
for (const metaKey of metadataKeys) {
|
|
113
|
+
await this._localCache.remove(metaKey);
|
|
114
|
+
}
|
|
115
|
+
console.info(`Cleaned up ${metadataKeys.length} old metadata entries, trying again`);
|
|
116
|
+
// Try again after cleanup
|
|
117
|
+
await this._localCache.set(key, value);
|
|
118
|
+
}
|
|
119
|
+
catch (cleanupError) {
|
|
120
|
+
// If it still fails after cleanup, log the error but continue
|
|
121
|
+
console.error('Failed to store metadata even after cleanup:', cleanupError);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
93
125
|
setMetadata(metadata) {
|
|
94
126
|
this._metadata = metadata;
|
|
95
127
|
this._registry = new PortableRegistry(metadata.latest, this.options.hasher);
|
|
@@ -273,4 +305,32 @@ export class BaseSubstrateClient extends JsonRpcClient {
|
|
|
273
305
|
setSigner(signer) {
|
|
274
306
|
this._options.signer = signer;
|
|
275
307
|
}
|
|
308
|
+
async queryMulti(queries, callback) {
|
|
309
|
+
// Extract keys from queries
|
|
310
|
+
const keys = queries.map((q) => q.fn.rawKey(...(q.args || [])));
|
|
311
|
+
const decodeValue = (query, rawValue) => {
|
|
312
|
+
// Get the QueryableStorage instance from the query function
|
|
313
|
+
const entry = new QueryableStorage(this.registry, query.meta.pallet, query.meta.name);
|
|
314
|
+
// Decode the value
|
|
315
|
+
return entry.decodeValue(rawValue);
|
|
316
|
+
};
|
|
317
|
+
// Create service directly when needed
|
|
318
|
+
let service;
|
|
319
|
+
if (this.rpcVersion === 'v2') {
|
|
320
|
+
service = new NewStorageQuery(this);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
service = new LegacyStorageQuery(this);
|
|
324
|
+
}
|
|
325
|
+
// If a callback is provided, set up a subscription
|
|
326
|
+
if (callback) {
|
|
327
|
+
return service.subscribe(keys, (results) => {
|
|
328
|
+
callback(queries.map((q, i) => decodeValue(q.fn, results[keys[i]])));
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
const results = await service.query(keys);
|
|
333
|
+
return queries.map((q, i) => decodeValue(q.fn, results[keys[i]]));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
276
336
|
}
|
package/client/DedotClient.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $H256, PortableRegistry } from '@dedot/codecs';
|
|
2
2
|
import { u32 } from '@dedot/shape';
|
|
3
3
|
import { assert, concatU8a, noop, twox64Concat, u8aToHex, xxhashAsU8a } from '@dedot/utils';
|
|
4
|
-
import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutorV2, StorageQueryExecutorV2, TxExecutorV2
|
|
4
|
+
import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutorV2, StorageQueryExecutorV2, TxExecutorV2 } from '../executor/index.js';
|
|
5
5
|
import { ChainHead, ChainSpec, Transaction, TransactionWatch } from '../json-rpc/index.js';
|
|
6
6
|
import { newProxyChain } from '../proxychain.js';
|
|
7
7
|
import { BaseSubstrateClient, ensurePresence } from './BaseSubstrateClient.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BlockHash, Option, StorageData, StorageKey } from '@dedot/codecs';
|
|
2
|
-
import type { AsyncMethod, Callback, GenericStorageQuery, GenericSubstrateApi, Unsub } from '@dedot/types';
|
|
3
|
-
import { QueryableStorage } from '../storage/
|
|
2
|
+
import type { AsyncMethod, Callback, GenericStorageQuery, GenericSubstrateApi, RpcVersion, Unsub } from '@dedot/types';
|
|
3
|
+
import { type BaseStorageQuery, QueryableStorage } from '../storage/index.js';
|
|
4
4
|
import { Executor } from './Executor.js';
|
|
5
5
|
/**
|
|
6
6
|
* @name StorageQueryExecutor
|
|
@@ -9,6 +9,7 @@ import { Executor } from './Executor.js';
|
|
|
9
9
|
export declare class StorageQueryExecutor<ChainApi extends GenericSubstrateApi = GenericSubstrateApi> extends Executor<ChainApi> {
|
|
10
10
|
doExecute(pallet: string, storage: string): GenericStorageQuery;
|
|
11
11
|
protected exposeStorageMapMethods(entry: QueryableStorage): Record<string, AsyncMethod>;
|
|
12
|
+
protected getStorageQuery(): BaseStorageQuery<RpcVersion>;
|
|
12
13
|
protected queryStorage(keys: StorageKey[], hash?: BlockHash): Promise<Record<StorageKey, Option<StorageData>>>;
|
|
13
14
|
protected subscribeStorage(keys: StorageKey[], callback: Callback<Array<StorageData | undefined>>): Promise<Unsub>;
|
|
14
15
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert, isFunction, isObject } from '@dedot/utils';
|
|
2
|
-
import { QueryableStorage } from '../storage/
|
|
2
|
+
import { LegacyStorageQuery, QueryableStorage } from '../storage/index.js';
|
|
3
3
|
import { Executor } from './Executor.js';
|
|
4
4
|
const DEFAULT_KEYS_PAGE_SIZE = 1000;
|
|
5
5
|
const DEFAULT_ENTRIES_PAGE_SIZE = 250;
|
|
@@ -98,22 +98,15 @@ export class StorageQueryExecutor extends Executor {
|
|
|
98
98
|
};
|
|
99
99
|
return { pagedKeys, pagedEntries };
|
|
100
100
|
}
|
|
101
|
+
getStorageQuery() {
|
|
102
|
+
return new LegacyStorageQuery(this.client);
|
|
103
|
+
}
|
|
101
104
|
async queryStorage(keys, hash) {
|
|
102
|
-
|
|
103
|
-
return changeSets[0].changes.reduce((o, [key, value]) => {
|
|
104
|
-
o[key] = value ?? undefined;
|
|
105
|
-
return o;
|
|
106
|
-
}, {});
|
|
105
|
+
return this.getStorageQuery().query(keys, hash);
|
|
107
106
|
}
|
|
108
107
|
subscribeStorage(keys, callback) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
changeSet.changes.forEach(([key, value]) => {
|
|
112
|
-
if (lastChanges[key] !== value) {
|
|
113
|
-
lastChanges[key] = value ?? undefined;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
return callback(keys.map((key) => lastChanges[key]));
|
|
108
|
+
return this.getStorageQuery().subscribe(keys, (results) => {
|
|
109
|
+
callback(keys.map((key) => results[key]));
|
|
117
110
|
});
|
|
118
111
|
}
|
|
119
112
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { BlockHash
|
|
2
|
-
import type { AsyncMethod,
|
|
3
|
-
import { HexString } from '@dedot/utils';
|
|
1
|
+
import { BlockHash } from '@dedot/codecs';
|
|
2
|
+
import type { AsyncMethod, GenericSubstrateApi, RpcVersion } from '@dedot/types';
|
|
4
3
|
import { ChainHead } from '../../json-rpc/index.js';
|
|
5
|
-
import { QueryableStorage } from '../../storage/
|
|
4
|
+
import { type BaseStorageQuery, QueryableStorage } from '../../storage/index.js';
|
|
6
5
|
import { ISubstrateClientAt } from '../../types.js';
|
|
7
6
|
import { StorageQueryExecutor } from '../StorageQueryExecutor.js';
|
|
8
7
|
/**
|
|
@@ -12,6 +11,5 @@ export declare class StorageQueryExecutorV2<ChainApi extends GenericSubstrateApi
|
|
|
12
11
|
chainHead: ChainHead;
|
|
13
12
|
constructor(client: ISubstrateClientAt<ChainApi>, chainHead: ChainHead, atBlockHash?: BlockHash);
|
|
14
13
|
protected exposeStorageMapMethods(entry: QueryableStorage): Record<string, AsyncMethod>;
|
|
15
|
-
protected
|
|
16
|
-
protected subscribeStorage(keys: HexString[], callback: Callback<Array<StorageData | undefined>>): Promise<() => Promise<void>>;
|
|
14
|
+
protected getStorageQuery(): BaseStorageQuery<RpcVersion>;
|
|
17
15
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from '@dedot/utils';
|
|
2
|
+
import { NewStorageQuery } from '../../storage/index.js';
|
|
2
3
|
import { StorageQueryExecutor } from '../StorageQueryExecutor.js';
|
|
3
4
|
/**
|
|
4
5
|
* @name StorageQueryExecutorV2
|
|
@@ -28,38 +29,11 @@ export class StorageQueryExecutorV2 extends StorageQueryExecutor {
|
|
|
28
29
|
};
|
|
29
30
|
return { entries };
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
async subscribeStorage(keys, callback) {
|
|
39
|
-
let best = await this.chainHead.bestBlock();
|
|
40
|
-
let eventToListen = 'bestBlock';
|
|
41
|
-
// TODO subscribe to finalized data source
|
|
42
|
-
// initialHash = this.chainHead.finalizedHash;
|
|
43
|
-
// eventToListen = 'finalizedBlock';
|
|
44
|
-
const latestChanges = new Map();
|
|
45
|
-
const pull = async ({ hash }) => {
|
|
46
|
-
const results = await this.queryStorage(keys, hash);
|
|
47
|
-
let changed = false;
|
|
48
|
-
keys.forEach((key) => {
|
|
49
|
-
const newValue = results[key];
|
|
50
|
-
if (latestChanges.size > 0 && latestChanges.get(key) === newValue)
|
|
51
|
-
return;
|
|
52
|
-
changed = true;
|
|
53
|
-
latestChanges.set(key, newValue);
|
|
54
|
-
});
|
|
55
|
-
if (!changed)
|
|
56
|
-
return;
|
|
57
|
-
callback(keys.map((key) => latestChanges.get(key)));
|
|
58
|
-
};
|
|
59
|
-
await pull(best);
|
|
60
|
-
const unsub = this.chainHead.on(eventToListen, pull);
|
|
61
|
-
return async () => {
|
|
62
|
-
unsub();
|
|
63
|
-
};
|
|
32
|
+
getStorageQuery() {
|
|
33
|
+
// @ts-ignore little trick to make querying data client.at instance works here,
|
|
34
|
+
// TODO need to rethink about this
|
|
35
|
+
if (!this.client['chainHead'])
|
|
36
|
+
this.client['chainHead'] = this.chainHead;
|
|
37
|
+
return new NewStorageQuery(this.client);
|
|
64
38
|
}
|
|
65
39
|
}
|
|
@@ -9,6 +9,12 @@ export declare class ExtraSignedExtension extends SignedExtension<any[], any[]>
|
|
|
9
9
|
get $Data(): $.AnyShape;
|
|
10
10
|
get $AdditionalSigned(): $.AnyShape;
|
|
11
11
|
get $Payload(): $.AnyShape;
|
|
12
|
+
/**
|
|
13
|
+
* Check if the extension requires no external inputs (e.g: struct or tuple with empty types like `()` or `[]`)
|
|
14
|
+
* @param extDef - The definition of the signed extension
|
|
15
|
+
* @returns boolean
|
|
16
|
+
*/
|
|
17
|
+
private isRequireNoExternalInputs;
|
|
12
18
|
toPayload(call?: HexString): SignerPayloadJSON;
|
|
13
19
|
toRawPayload(call?: HexString): SignerPayloadRaw;
|
|
14
20
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as $ from '@dedot/shape';
|
|
2
|
-
import {
|
|
2
|
+
import { ensurePresence, u8aToHex } from '@dedot/utils';
|
|
3
3
|
import { SignedExtension } from './SignedExtension.js';
|
|
4
|
+
import { FallbackSignedExtension, isEmptyStructOrTuple } from './FallbackSignedExtension.js';
|
|
4
5
|
import { knownSignedExtensions } from './known/index.js';
|
|
5
6
|
export class ExtraSignedExtension extends SignedExtension {
|
|
6
7
|
#signedExtensions;
|
|
@@ -34,13 +35,31 @@ export class ExtraSignedExtension extends SignedExtension {
|
|
|
34
35
|
const { signedExtensions: userSignedExtensions = {} } = this.client.options;
|
|
35
36
|
const Extension = userSignedExtensions[extDef.ident] ||
|
|
36
37
|
knownSignedExtensions[extDef.ident];
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (Extension) {
|
|
39
|
+
return new Extension(this.client, {
|
|
40
|
+
...ensurePresence(this.options),
|
|
41
|
+
def: extDef,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else if (this.isRequireNoExternalInputs(extDef)) {
|
|
45
|
+
return new FallbackSignedExtension(this.client, {
|
|
46
|
+
...ensurePresence(this.options),
|
|
47
|
+
def: extDef,
|
|
48
|
+
}, extDef.ident);
|
|
49
|
+
}
|
|
50
|
+
// For extensions that require input but aren't implemented, throw an error
|
|
51
|
+
throw new Error(`SignedExtension for ${extDef.ident} requires input but is not implemented`);
|
|
42
52
|
});
|
|
43
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if the extension requires no external inputs (e.g: struct or tuple with empty types like `()` or `[]`)
|
|
56
|
+
* @param extDef - The definition of the signed extension
|
|
57
|
+
* @returns boolean
|
|
58
|
+
*/
|
|
59
|
+
isRequireNoExternalInputs(extDef) {
|
|
60
|
+
return (isEmptyStructOrTuple(this.registry, extDef.typeId) &&
|
|
61
|
+
isEmptyStructOrTuple(this.registry, extDef.additionalSigned));
|
|
62
|
+
}
|
|
44
63
|
toPayload(call = '0x') {
|
|
45
64
|
const signedExtensions = this.#signedExtensions.map((se) => se.identifier);
|
|
46
65
|
const { version } = this.registry.metadata.extrinsic;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PortableRegistry } from '@dedot/codecs';
|
|
2
|
+
import { SignerPayloadJSON } from '@dedot/types';
|
|
3
|
+
import { SignedExtension } from './SignedExtension.js';
|
|
4
|
+
import { ISubstrateClient } from '../../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* A fallback signed extension that can be used for extensions
|
|
7
|
+
* that don't require external input.
|
|
8
|
+
*
|
|
9
|
+
* This extension is automatically used for:
|
|
10
|
+
* - Unknown extensions with empty struct or tuple types
|
|
11
|
+
* - Known extensions that don't require input, such as:
|
|
12
|
+
* - CheckNonZeroSender: Ensures sender is not the zero address
|
|
13
|
+
* - CheckWeight: Block resource (weight) limit check
|
|
14
|
+
* - PrevalidateAttests: Validates `attest` calls prior to execution
|
|
15
|
+
* - StorageWeightReclaim: Storage weight reclaim mechanism
|
|
16
|
+
*
|
|
17
|
+
* These extensions have empty struct or tuple types and don't need explicit implementation.
|
|
18
|
+
*/
|
|
19
|
+
export declare class FallbackSignedExtension extends SignedExtension {
|
|
20
|
+
private readonly extensionIdent;
|
|
21
|
+
constructor(client: ISubstrateClient, options: any, extensionIdent: string);
|
|
22
|
+
get identifier(): string;
|
|
23
|
+
init(): Promise<void>;
|
|
24
|
+
toPayload(): Partial<SignerPayloadJSON>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Checks if a type is an empty struct or tuple (doesn't require external input).
|
|
28
|
+
*
|
|
29
|
+
* @param registry The portable registry
|
|
30
|
+
* @param typeId The type ID to check
|
|
31
|
+
* @returns True if the type is an empty struct or tuple, false otherwise
|
|
32
|
+
*/
|
|
33
|
+
export declare function isEmptyStructOrTuple(registry: PortableRegistry, typeId: number): boolean;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SignedExtension } from './SignedExtension.js';
|
|
2
|
+
/**
|
|
3
|
+
* A fallback signed extension that can be used for extensions
|
|
4
|
+
* that don't require external input.
|
|
5
|
+
*
|
|
6
|
+
* This extension is automatically used for:
|
|
7
|
+
* - Unknown extensions with empty struct or tuple types
|
|
8
|
+
* - Known extensions that don't require input, such as:
|
|
9
|
+
* - CheckNonZeroSender: Ensures sender is not the zero address
|
|
10
|
+
* - CheckWeight: Block resource (weight) limit check
|
|
11
|
+
* - PrevalidateAttests: Validates `attest` calls prior to execution
|
|
12
|
+
* - StorageWeightReclaim: Storage weight reclaim mechanism
|
|
13
|
+
*
|
|
14
|
+
* These extensions have empty struct or tuple types and don't need explicit implementation.
|
|
15
|
+
*/
|
|
16
|
+
export class FallbackSignedExtension extends SignedExtension {
|
|
17
|
+
extensionIdent;
|
|
18
|
+
constructor(client, options, extensionIdent) {
|
|
19
|
+
super(client, options);
|
|
20
|
+
this.extensionIdent = extensionIdent;
|
|
21
|
+
}
|
|
22
|
+
get identifier() {
|
|
23
|
+
return this.extensionIdent;
|
|
24
|
+
}
|
|
25
|
+
async init() {
|
|
26
|
+
// Initialize with empty data and additionalSigned
|
|
27
|
+
this.data = {};
|
|
28
|
+
this.additionalSigned = [];
|
|
29
|
+
}
|
|
30
|
+
toPayload() {
|
|
31
|
+
return {}; // No payload contribution
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Checks if a type is an empty struct or tuple (doesn't require external input).
|
|
36
|
+
*
|
|
37
|
+
* @param registry The portable registry
|
|
38
|
+
* @param typeId The type ID to check
|
|
39
|
+
* @returns True if the type is an empty struct or tuple, false otherwise
|
|
40
|
+
*/
|
|
41
|
+
export function isEmptyStructOrTuple(registry, typeId) {
|
|
42
|
+
try {
|
|
43
|
+
const type = registry.findType(typeId);
|
|
44
|
+
// Check if it's an empty struct
|
|
45
|
+
if (type.typeDef.type === 'Struct' && type.typeDef.value.fields.length === 0) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
// Check if it's an empty tuple
|
|
49
|
+
if (type.typeDef.type === 'Tuple' && type.typeDef.value.fields.length === 0) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Ignore errors
|
|
55
|
+
}
|
|
56
|
+
// All other cases (including errors) require input
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known signed extensions that require external input.
|
|
3
|
+
*
|
|
4
|
+
* Note: Extensions that don't require external input are automatically handled by FallbackSignedExtension:
|
|
5
|
+
* - CheckNonZeroSender
|
|
6
|
+
* - CheckWeight
|
|
7
|
+
* - PrevalidateAttests
|
|
8
|
+
* - StorageWeightReclaim
|
|
9
|
+
*
|
|
10
|
+
* These extensions have empty struct or tuple types and don't need explicit implementation.
|
|
11
|
+
*/
|
|
1
12
|
import { ISignedExtension } from '../SignedExtension.js';
|
|
2
13
|
export type AnySignedExtension = new (...args: any[]) => ISignedExtension;
|
|
3
14
|
export declare const knownSignedExtensions: Record<string, AnySignedExtension>;
|
|
@@ -1,26 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known signed extensions that require external input.
|
|
3
|
+
*
|
|
4
|
+
* Note: Extensions that don't require external input are automatically handled by FallbackSignedExtension:
|
|
5
|
+
* - CheckNonZeroSender
|
|
6
|
+
* - CheckWeight
|
|
7
|
+
* - PrevalidateAttests
|
|
8
|
+
* - StorageWeightReclaim
|
|
9
|
+
*
|
|
10
|
+
* These extensions have empty struct or tuple types and don't need explicit implementation.
|
|
11
|
+
*/
|
|
1
12
|
import { ChargeAssetTxPayment } from './ChargeAssetTxPayment.js';
|
|
2
13
|
import { ChargeTransactionPayment } from './ChargeTransactionPayment.js';
|
|
3
14
|
import { CheckGenesis } from './CheckGenesis.js';
|
|
4
15
|
import { CheckMetadataHash } from './CheckMetadataHash.js';
|
|
5
16
|
import { CheckMortality } from './CheckMortality.js';
|
|
6
|
-
import { CheckNonZeroSender } from './CheckNonZeroSender.js';
|
|
7
17
|
import { CheckNonce } from './CheckNonce.js';
|
|
8
18
|
import { CheckSpecVersion } from './CheckSpecVersion.js';
|
|
9
19
|
import { CheckTxVersion } from './CheckTxVersion.js';
|
|
10
|
-
import { CheckWeight } from './CheckWeight.js';
|
|
11
|
-
import { PrevalidateAttests } from './PrevalidateAttests.js';
|
|
12
|
-
import { StorageWeightReclaim } from './StorageWeightReclaim.js';
|
|
13
20
|
export const knownSignedExtensions = {
|
|
14
|
-
CheckNonZeroSender,
|
|
15
21
|
CheckSpecVersion,
|
|
16
22
|
CheckTxVersion,
|
|
17
23
|
CheckGenesis,
|
|
18
24
|
CheckMortality,
|
|
19
25
|
CheckNonce,
|
|
20
|
-
CheckWeight,
|
|
21
26
|
ChargeTransactionPayment,
|
|
22
|
-
PrevalidateAttests,
|
|
23
27
|
ChargeAssetTxPayment,
|
|
24
28
|
CheckMetadataHash,
|
|
25
|
-
StorageWeightReclaim,
|
|
26
29
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dedot/api",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2-next.6cfafabd.24+6cfafab",
|
|
4
4
|
"description": "A delightful JavaScript/TypeScript client for Polkadot & Substrate",
|
|
5
5
|
"author": "Thang X. Vu <thang@coongcrafts.io>",
|
|
6
6
|
"homepage": "https://github.com/dedotdev/dedot/tree/main/packages/api",
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"sideEffects": false,
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@dedot/codecs": "0.8.
|
|
17
|
-
"@dedot/providers": "0.8.
|
|
18
|
-
"@dedot/runtime-specs": "0.8.
|
|
19
|
-
"@dedot/shape": "0.8.
|
|
20
|
-
"@dedot/storage": "0.8.
|
|
21
|
-
"@dedot/types": "0.8.
|
|
22
|
-
"@dedot/utils": "0.8.
|
|
16
|
+
"@dedot/codecs": "0.8.2-next.6cfafabd.24+6cfafab",
|
|
17
|
+
"@dedot/providers": "0.8.2-next.6cfafabd.24+6cfafab",
|
|
18
|
+
"@dedot/runtime-specs": "0.8.2-next.6cfafabd.24+6cfafab",
|
|
19
|
+
"@dedot/shape": "0.8.2-next.6cfafabd.24+6cfafab",
|
|
20
|
+
"@dedot/storage": "0.8.2-next.6cfafabd.24+6cfafab",
|
|
21
|
+
"@dedot/types": "0.8.2-next.6cfafabd.24+6cfafab",
|
|
22
|
+
"@dedot/utils": "0.8.2-next.6cfafabd.24+6cfafab"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json",
|
|
26
26
|
"clean": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo ./tsconfig.build.tsbuildinfo",
|
|
27
|
-
"test": "vitest --watch=false"
|
|
27
|
+
"test": "npx vitest --watch=false"
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=18"
|
|
49
49
|
},
|
|
50
50
|
"license": "Apache-2.0",
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "6cfafabd52dbc7e5bd3ded135e9617fc412bba2b",
|
|
52
52
|
"module": "./index.js",
|
|
53
53
|
"types": "./index.d.ts"
|
|
54
54
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BlockHash, StorageData, StorageKey } from '@dedot/codecs';
|
|
2
|
+
import type { Callback, RpcVersion, Unsub, VersionedGenericSubstrateApi } from '@dedot/types';
|
|
3
|
+
import type { SubstrateApi } from '../chaintypes/index.js';
|
|
4
|
+
import { BaseSubstrateClient } from '../client/BaseSubstrateClient.js';
|
|
5
|
+
/**
|
|
6
|
+
* @name BaseStorageQuery
|
|
7
|
+
* @description
|
|
8
|
+
* An abstract service that provides functionality for querying and subscribing to
|
|
9
|
+
* multiple storage items. This service is designed to be extended by version-specific
|
|
10
|
+
* implementations that handle the details of interacting with different JSON-RPC APIs.
|
|
11
|
+
*
|
|
12
|
+
* The service provides a simple interface for:
|
|
13
|
+
* - Querying multiple storage keys in a single call
|
|
14
|
+
* - Subscribing to changes in multiple storage keys
|
|
15
|
+
*
|
|
16
|
+
* This abstraction eliminates code duplication between different client implementations
|
|
17
|
+
* and provides a consistent interface for storage operations.
|
|
18
|
+
*/
|
|
19
|
+
export declare abstract class BaseStorageQuery<Rv extends RpcVersion, ChainApi extends VersionedGenericSubstrateApi = SubstrateApi, T extends BaseSubstrateClient<Rv, ChainApi> = BaseSubstrateClient<Rv, ChainApi>> {
|
|
20
|
+
protected client: T;
|
|
21
|
+
/**
|
|
22
|
+
* @param client - The substrate client instance
|
|
23
|
+
*/
|
|
24
|
+
constructor(client: T);
|
|
25
|
+
/**
|
|
26
|
+
* Query multiple storage items in a single call
|
|
27
|
+
*
|
|
28
|
+
* @param keys - Array of storage keys to query
|
|
29
|
+
* @param at - Optional block hash to query at
|
|
30
|
+
* @returns Promise resolving to a record mapping storage keys to their values
|
|
31
|
+
*/
|
|
32
|
+
abstract query(keys: StorageKey[], at?: BlockHash): Promise<Record<StorageKey, StorageData | undefined>>;
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to multiple storage items
|
|
35
|
+
*
|
|
36
|
+
* @param keys - Array of storage keys to subscribe to
|
|
37
|
+
* @param callback - Function to call when storage values change
|
|
38
|
+
* @returns Promise resolving to an unsubscribe function
|
|
39
|
+
*/
|
|
40
|
+
abstract subscribe(keys: StorageKey[], callback: Callback<Record<StorageKey, StorageData | undefined>>): Promise<Unsub>;
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name BaseStorageQuery
|
|
3
|
+
* @description
|
|
4
|
+
* An abstract service that provides functionality for querying and subscribing to
|
|
5
|
+
* multiple storage items. This service is designed to be extended by version-specific
|
|
6
|
+
* implementations that handle the details of interacting with different JSON-RPC APIs.
|
|
7
|
+
*
|
|
8
|
+
* The service provides a simple interface for:
|
|
9
|
+
* - Querying multiple storage keys in a single call
|
|
10
|
+
* - Subscribing to changes in multiple storage keys
|
|
11
|
+
*
|
|
12
|
+
* This abstraction eliminates code duplication between different client implementations
|
|
13
|
+
* and provides a consistent interface for storage operations.
|
|
14
|
+
*/
|
|
15
|
+
export class BaseStorageQuery {
|
|
16
|
+
client;
|
|
17
|
+
/**
|
|
18
|
+
* @param client - The substrate client instance
|
|
19
|
+
*/
|
|
20
|
+
constructor(client) {
|
|
21
|
+
this.client = client;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { BlockHash, StorageData, StorageKey } from '@dedot/codecs';
|
|
2
|
+
import type { Callback, RpcLegacy, Unsub, VersionedGenericSubstrateApi } from '@dedot/types';
|
|
3
|
+
import type { SubstrateApi } from '../chaintypes/index.js';
|
|
4
|
+
import { LegacyClient } from '../client/LegacyClient.js';
|
|
5
|
+
import { BaseStorageQuery } from './BaseStorageQuery.js';
|
|
6
|
+
/**
|
|
7
|
+
* @name LegacyStorageQuery
|
|
8
|
+
* @description
|
|
9
|
+
* Implementation of BaseStorageQuery for the legacy JSON-RPC API (v1).
|
|
10
|
+
* This service handles storage queries using the state_queryStorageAt and
|
|
11
|
+
* state_subscribeStorage RPC methods.
|
|
12
|
+
*
|
|
13
|
+
* It provides:
|
|
14
|
+
* - One-time queries using state_queryStorageAt
|
|
15
|
+
* - Subscriptions using state_subscribeStorage
|
|
16
|
+
* - Efficient change tracking for subscriptions
|
|
17
|
+
*/
|
|
18
|
+
export declare class LegacyStorageQuery<ChainApi extends VersionedGenericSubstrateApi = SubstrateApi> extends BaseStorageQuery<RpcLegacy, ChainApi, LegacyClient<ChainApi>> {
|
|
19
|
+
/**
|
|
20
|
+
* Query multiple storage items in a single call using state_queryStorageAt
|
|
21
|
+
*
|
|
22
|
+
* @param keys - Array of storage keys to query
|
|
23
|
+
* @param at - Optional block hash to query at (defaults to current/best block)
|
|
24
|
+
* @returns Promise resolving to a record mapping storage keys to their values
|
|
25
|
+
*/
|
|
26
|
+
query(keys: StorageKey[], at?: BlockHash): Promise<Record<StorageKey, StorageData | undefined>>;
|
|
27
|
+
/**
|
|
28
|
+
* Subscribe to multiple storage items using state_subscribeStorage
|
|
29
|
+
*
|
|
30
|
+
* @param keys - Array of storage keys to subscribe to
|
|
31
|
+
* @param callback - Function to call when storage values change
|
|
32
|
+
* @returns Promise resolving to an unsubscribe function
|
|
33
|
+
*/
|
|
34
|
+
subscribe(keys: StorageKey[], callback: Callback<Record<StorageKey, StorageData | undefined>>): Promise<Unsub>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { BaseStorageQuery } from './BaseStorageQuery.js';
|
|
2
|
+
/**
|
|
3
|
+
* @name LegacyStorageQuery
|
|
4
|
+
* @description
|
|
5
|
+
* Implementation of BaseStorageQuery for the legacy JSON-RPC API (v1).
|
|
6
|
+
* This service handles storage queries using the state_queryStorageAt and
|
|
7
|
+
* state_subscribeStorage RPC methods.
|
|
8
|
+
*
|
|
9
|
+
* It provides:
|
|
10
|
+
* - One-time queries using state_queryStorageAt
|
|
11
|
+
* - Subscriptions using state_subscribeStorage
|
|
12
|
+
* - Efficient change tracking for subscriptions
|
|
13
|
+
*/
|
|
14
|
+
export class LegacyStorageQuery extends BaseStorageQuery {
|
|
15
|
+
/**
|
|
16
|
+
* Query multiple storage items in a single call using state_queryStorageAt
|
|
17
|
+
*
|
|
18
|
+
* @param keys - Array of storage keys to query
|
|
19
|
+
* @param at - Optional block hash to query at (defaults to current/best block)
|
|
20
|
+
* @returns Promise resolving to a record mapping storage keys to their values
|
|
21
|
+
*/
|
|
22
|
+
async query(keys, at) {
|
|
23
|
+
// Query storage at the specified block or current block
|
|
24
|
+
const changeSets = at
|
|
25
|
+
? await this.client.rpc.state_queryStorageAt(keys, at)
|
|
26
|
+
: await this.client.rpc.state_queryStorageAt(keys);
|
|
27
|
+
// Create a map of key -> value for easy lookup
|
|
28
|
+
const results = {};
|
|
29
|
+
// Initialize all keys with undefined
|
|
30
|
+
keys.forEach(key => results[key] = undefined);
|
|
31
|
+
// Update with actual values from the response
|
|
32
|
+
if (changeSets && changeSets.length > 0) {
|
|
33
|
+
changeSets[0].changes.forEach(([key, value]) => {
|
|
34
|
+
results[key] = value ?? undefined;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Subscribe to multiple storage items using state_subscribeStorage
|
|
41
|
+
*
|
|
42
|
+
* @param keys - Array of storage keys to subscribe to
|
|
43
|
+
* @param callback - Function to call when storage values change
|
|
44
|
+
* @returns Promise resolving to an unsubscribe function
|
|
45
|
+
*/
|
|
46
|
+
async subscribe(keys, callback) {
|
|
47
|
+
// Track the latest changes for each key
|
|
48
|
+
const lastChanges = {};
|
|
49
|
+
// Initialize all keys with undefined
|
|
50
|
+
keys.forEach(key => lastChanges[key] = undefined);
|
|
51
|
+
// Subscribe to storage changes
|
|
52
|
+
return this.client.rpc.state_subscribeStorage(keys, (changeSet) => {
|
|
53
|
+
// Update the latest changes
|
|
54
|
+
changeSet.changes.forEach(([key, value]) => {
|
|
55
|
+
if (lastChanges[key] !== value) {
|
|
56
|
+
lastChanges[key] = value ?? undefined;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
// Call the callback with the updated map
|
|
60
|
+
callback({ ...lastChanges });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|