@acala-network/chopsticks-core 0.16.2 → 1.0.0

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.
@@ -492,6 +492,7 @@ class Blockchain {
492
492
  function registerBlock(block) {
493
493
  // if exceed max memory block count, delete the oldest block
494
494
  if (_class_private_field_get(this, _blocksByNumber).size === _class_private_field_get(this, _maxMemoryBlockCount)) {
495
+ // #blocksByNumber can't be empty so the force unwrap is safe
495
496
  const { hash, number } = _class_private_field_get(this, _blocksByNumber).values().next().value;
496
497
  _class_private_field_get(this, _blocksByNumber).delete(number);
497
498
  _class_private_field_get(this, _blocksByHash).delete(hash);
@@ -82,11 +82,11 @@ const logger = _logger.defaultLogger.child({
82
82
  name: 'layer'
83
83
  });
84
84
  const BATCH_SIZE = 1000;
85
- var StorageValueKind;
86
- (function(StorageValueKind) {
85
+ var StorageValueKind = /*#__PURE__*/ function(StorageValueKind) {
87
86
  StorageValueKind["Deleted"] = "Deleted";
88
87
  StorageValueKind["DeletedPrefix"] = "DeletedPrefix";
89
- })(StorageValueKind || (StorageValueKind = {}));
88
+ return StorageValueKind;
89
+ }({});
90
90
  var _api = /*#__PURE__*/ new WeakMap(), _at = /*#__PURE__*/ new WeakMap(), _db = /*#__PURE__*/ new WeakMap(), _keyCache = /*#__PURE__*/ new WeakMap(), _defaultChildKeyCache = /*#__PURE__*/ new WeakMap();
91
91
  class RemoteStorageLayer {
92
92
  async get(key, _cache) {
@@ -97,12 +97,12 @@ const logger = _logger.defaultLogger.child({
97
97
  name: 'txpool'
98
98
  });
99
99
  const APPLY_EXTRINSIC_ERROR = 'TxPool::ApplyExtrinsicError';
100
- var BuildBlockMode;
101
- (function(BuildBlockMode) {
100
+ var BuildBlockMode = /*#__PURE__*/ function(BuildBlockMode) {
102
101
  /** One block per batch (default) */ BuildBlockMode["Batch"] = "Batch";
103
102
  /** One block per tx */ BuildBlockMode["Instant"] = "Instant";
104
103
  /** Only build when triggered */ BuildBlockMode["Manual"] = "Manual";
105
- })(BuildBlockMode || (BuildBlockMode = {}));
104
+ return BuildBlockMode;
105
+ }({});
106
106
  var _chain = /*#__PURE__*/ new WeakMap(), _pool = /*#__PURE__*/ new WeakMap(), _ump = /*#__PURE__*/ new WeakMap(), _dmp = /*#__PURE__*/ new WeakMap(), _hrmp = /*#__PURE__*/ new WeakMap(), _mode = /*#__PURE__*/ new WeakMap(), _inherentProviders = /*#__PURE__*/ new WeakMap(), _pendingBlocks = /*#__PURE__*/ new WeakMap(), _isBuilding = /*#__PURE__*/ new WeakMap(), _getSigner = /*#__PURE__*/ new WeakSet(), _maybeBuildBlock = /*#__PURE__*/ new WeakSet(), _batchBuildBlock = /*#__PURE__*/ new WeakMap(), _buildBlockIfNeeded = /*#__PURE__*/ new WeakSet(), _buildBlock = /*#__PURE__*/ new WeakSet();
107
107
  class TxPool {
108
108
  get pendingExtrinsics() {
@@ -1,4 +1,4 @@
1
1
  import { pino } from 'pino';
2
- export declare const pinoLogger: import("pino").Logger<never>;
3
- export declare const defaultLogger: pino.Logger<never>;
2
+ export declare const pinoLogger: import("pino").Logger<never, boolean>;
3
+ export declare const defaultLogger: pino.Logger<never, boolean>;
4
4
  export declare const truncate: (val: any) => any;
@@ -73,6 +73,12 @@ export type LimitReached = {
73
73
  * @return OperationStarted event with operationId to receive the result on the follow subscription
74
74
  */
75
75
  export declare const chainHead_v1_body: Handler<[string, HexString], OperationStarted | LimitReached>;
76
+ /**
77
+ * Resume an operation paused through `operationWaitingForContinue`
78
+ *
79
+ * @param context
80
+ * @param params - [`followSubscription`, `operationId`]
81
+ */
76
82
  export declare const chainHead_v1_continue: Handler<[string, HexString], null>;
77
83
  export declare const chainHead_v1_stopOperation: Handler<[string, HexString], null>;
78
84
  export declare const chainHead_v1_unpin: Handler<[string, HexString | HexString[]], null>;
@@ -42,7 +42,7 @@ const _logger = require("../../logger.js");
42
42
  const logger = _logger.defaultLogger.child({
43
43
  name: 'rpc-chainHead_v1'
44
44
  });
45
- const callbacks = new Map();
45
+ const following = new Map();
46
46
  async function afterResponse(fn) {
47
47
  await new Promise((resolve)=>setTimeout(resolve, 0));
48
48
  fn();
@@ -82,10 +82,13 @@ const chainHead_v1_follow = async (context, [withRuntime], { subscribe })=>{
82
82
  const id = context.chain.headState.subscribeHead(update);
83
83
  const cleanup = ()=>{
84
84
  context.chain.headState.unsubscribeHead(id);
85
- callbacks.delete(id);
85
+ following.delete(id);
86
86
  };
87
87
  const callback = subscribe('chainHead_v1_followEvent', id, cleanup);
88
- callbacks.set(id, callback);
88
+ following.set(id, {
89
+ callback,
90
+ pendingDescendantValues: new Map()
91
+ });
89
92
  afterResponse(async ()=>{
90
93
  callback({
91
94
  event: 'initialized',
@@ -102,7 +105,7 @@ const chainHead_v1_unfollow = async (_, [followSubscription], { unsubscribe })=>
102
105
  return null;
103
106
  };
104
107
  const chainHead_v1_header = async (context, [followSubscription, hash])=>{
105
- if (!callbacks.has(followSubscription)) return null;
108
+ if (!following.has(followSubscription)) return null;
106
109
  const block = await context.chain.getBlock(hash);
107
110
  return block ? (await block.header).toHex() : null;
108
111
  };
@@ -116,7 +119,7 @@ const chainHead_v1_call = async (context, [followSubscription, hash, method, cal
116
119
  afterResponse(async ()=>{
117
120
  const block = await context.chain.getBlock(hash);
118
121
  if (!block) {
119
- callbacks.get(followSubscription)?.({
122
+ following.get(followSubscription)?.callback({
120
123
  event: 'operationError',
121
124
  operationId,
122
125
  error: `Block ${hash} not found`
@@ -126,13 +129,13 @@ const chainHead_v1_call = async (context, [followSubscription, hash, method, cal
126
129
  const resp = await block.call(method, [
127
130
  callParameters
128
131
  ]);
129
- callbacks.get(followSubscription)?.({
132
+ following.get(followSubscription)?.callback({
130
133
  event: 'operationCallDone',
131
134
  operationId,
132
135
  output: resp.result
133
136
  });
134
137
  } catch (ex) {
135
- callbacks.get(followSubscription)?.({
138
+ following.get(followSubscription)?.callback({
136
139
  event: 'operationError',
137
140
  operationId,
138
141
  error: ex.message
@@ -142,12 +145,36 @@ const chainHead_v1_call = async (context, [followSubscription, hash, method, cal
142
145
  });
143
146
  return operationStarted(operationId);
144
147
  };
148
+ const PAGE_SIZE = 1000;
149
+ async function getDescendantValues(block, params) {
150
+ const keys = await block.getKeysPaged({
151
+ ...params,
152
+ pageSize: PAGE_SIZE
153
+ });
154
+ const items = await Promise.all(keys.map((key)=>block.get(key).then((value)=>({
155
+ key,
156
+ value
157
+ }))));
158
+ if (keys.length < PAGE_SIZE) {
159
+ return {
160
+ items,
161
+ next: null
162
+ };
163
+ }
164
+ return {
165
+ items,
166
+ next: {
167
+ ...params,
168
+ startKey: keys[PAGE_SIZE - 1]
169
+ }
170
+ };
171
+ }
145
172
  const chainHead_v1_storage = async (context, [followSubscription, hash, items, _childTrie])=>{
146
173
  const operationId = randomId();
147
174
  afterResponse(async ()=>{
148
175
  const block = await context.chain.getBlock(hash);
149
176
  if (!block) {
150
- callbacks.get(followSubscription)?.({
177
+ following.get(followSubscription)?.callback({
151
178
  event: 'operationError',
152
179
  operationId,
153
180
  error: 'Block not found'
@@ -160,7 +187,7 @@ const chainHead_v1_storage = async (context, [followSubscription, hash, items, _
160
187
  {
161
188
  const value = await block.get(sir.key);
162
189
  if (value) {
163
- callbacks.get(followSubscription)?.({
190
+ following.get(followSubscription)?.callback({
164
191
  event: 'operationStorageItems',
165
192
  operationId,
166
193
  items: [
@@ -171,43 +198,47 @@ const chainHead_v1_storage = async (context, [followSubscription, hash, items, _
171
198
  ]
172
199
  });
173
200
  }
174
- break;
201
+ return null;
175
202
  }
176
203
  case 'descendantsValues':
177
204
  {
178
- // TODO expose pagination
179
- const pageSize = 100;
180
- let startKey = '0x';
181
- while(startKey){
182
- const keys = await block.getKeysPaged({
183
- prefix: sir.key,
184
- pageSize,
185
- startKey
186
- });
187
- startKey = keys[pageSize - 1] ?? null;
188
- const items = await Promise.all(keys.map((key)=>block.get(key).then((value)=>({
189
- key,
190
- value
191
- }))));
192
- callbacks.get(followSubscription)?.({
193
- event: 'operationStorageItems',
194
- operationId,
195
- items
196
- });
197
- break;
198
- }
199
- break;
205
+ const { items, next } = await getDescendantValues(block, {
206
+ prefix: sir.key,
207
+ startKey: '0x'
208
+ });
209
+ following.get(followSubscription)?.callback({
210
+ event: 'operationStorageItems',
211
+ operationId,
212
+ items
213
+ });
214
+ return next;
200
215
  }
201
216
  default:
202
217
  // TODO
203
218
  console.warn(`Storage type not implemented ${sir.type}`);
219
+ return null;
204
220
  }
205
221
  };
206
- await Promise.all(items.map(handleStorageItemRequest));
207
- callbacks.get(followSubscription)?.({
208
- event: 'operationStorageDone',
209
- operationId
210
- });
222
+ const listResult = await Promise.all(items.map(handleStorageItemRequest));
223
+ const pending = listResult.filter((v)=>v !== null);
224
+ if (!pending.length) {
225
+ following.get(followSubscription)?.callback({
226
+ event: 'operationStorageDone',
227
+ operationId
228
+ });
229
+ } else {
230
+ const follower = following.get(followSubscription);
231
+ if (follower) {
232
+ follower.pendingDescendantValues.set(operationId, {
233
+ hash,
234
+ params: pending
235
+ });
236
+ follower.callback({
237
+ event: 'operationWaitingForContinue',
238
+ operationId
239
+ });
240
+ }
241
+ }
211
242
  });
212
243
  return {
213
244
  ...operationStarted(operationId),
@@ -218,7 +249,7 @@ const limitReached = {
218
249
  result: 'limitReached'
219
250
  };
220
251
  const chainHead_v1_body = async (context, [followSubscription, hash])=>{
221
- if (!callbacks.has(followSubscription)) return limitReached;
252
+ if (!following.has(followSubscription)) return limitReached;
222
253
  const block = await context.chain.getBlock(hash);
223
254
  if (!block) {
224
255
  throw new _shared.ResponseError(-32801, 'Block not found');
@@ -226,7 +257,7 @@ const chainHead_v1_body = async (context, [followSubscription, hash])=>{
226
257
  const operationId = randomId();
227
258
  afterResponse(async ()=>{
228
259
  const body = await block.extrinsics;
229
- callbacks.get(followSubscription)?.({
260
+ following.get(followSubscription)?.callback({
230
261
  event: 'operationBodyDone',
231
262
  operationId,
232
263
  value: body
@@ -234,10 +265,49 @@ const chainHead_v1_body = async (context, [followSubscription, hash])=>{
234
265
  });
235
266
  return operationStarted(operationId);
236
267
  };
237
- const chainHead_v1_continue = async (_context, [_followSubscription, _operationId])=>{
268
+ const chainHead_v1_continue = async (context, [followSubscription, operationId])=>{
269
+ const follower = following.get(followSubscription);
270
+ const pendingOp = follower?.pendingDescendantValues.get(operationId);
271
+ if (!pendingOp || !follower) {
272
+ throw new _shared.ResponseError(-32803, "Operation ID doesn't have anything pending");
273
+ }
274
+ const block = await context.chain.getBlock(pendingOp.hash);
275
+ if (!block) {
276
+ throw new _shared.ResponseError(-32801, 'Block not found');
277
+ }
278
+ afterResponse(async ()=>{
279
+ const handlePendingOperation = async (params)=>{
280
+ const { items, next } = await getDescendantValues(block, params);
281
+ follower.callback({
282
+ event: 'operationStorageItems',
283
+ operationId,
284
+ items
285
+ });
286
+ return next;
287
+ };
288
+ const listResult = await Promise.all(pendingOp.params.map(handlePendingOperation));
289
+ const pending = listResult.filter((v)=>v !== null);
290
+ if (!pending.length) {
291
+ follower.pendingDescendantValues.delete(operationId);
292
+ follower.callback({
293
+ event: 'operationStorageDone',
294
+ operationId
295
+ });
296
+ } else {
297
+ follower.pendingDescendantValues.set(operationId, {
298
+ hash: pendingOp.hash,
299
+ params: pending
300
+ });
301
+ follower.callback({
302
+ event: 'operationWaitingForContinue',
303
+ operationId
304
+ });
305
+ }
306
+ });
238
307
  return null;
239
308
  };
240
- const chainHead_v1_stopOperation = async (_context, [_followSubscription, _operationId])=>{
309
+ const chainHead_v1_stopOperation = async (_context, [followSubscription, operationId])=>{
310
+ following.get(followSubscription)?.pendingDescendantValues.delete(operationId);
241
311
  return null;
242
312
  };
243
313
  const chainHead_v1_unpin = async (_context, [_followSubscription, _hashOrHashes])=>{
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { Blockchain } from '../blockchain/index.js';
3
- export declare const logger: import("pino").default.Logger<never>;
3
+ export declare const logger: import("pino").default.Logger<never, boolean>;
4
4
  export declare const zHex: z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>;
5
5
  export declare const zHash: z.ZodIntersection<z.ZodString, z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>>;
6
6
  export declare class ResponseError extends Error {
@@ -50,8 +50,7 @@ class ResponseError extends Error {
50
50
  };
51
51
  }
52
52
  constructor(code, message){
53
- super(message);
54
- _define_property(this, "code", void 0);
53
+ super(message), _define_property(this, "code", void 0);
55
54
  this.name = 'ResponseError';
56
55
  this.code = code;
57
56
  }
@@ -111,7 +111,7 @@ const isUrl = (url)=>{
111
111
  try {
112
112
  new URL(url);
113
113
  return true;
114
- } catch (e) {
114
+ } catch (_e) {
115
115
  return false;
116
116
  }
117
117
  };
@@ -1,4 +1,4 @@
1
1
  import { Blockchain } from '../blockchain/index.js';
2
- export declare const xcmLogger: import("pino").default.Logger<never>;
2
+ export declare const xcmLogger: import("pino").default.Logger<never, boolean>;
3
3
  export declare const connectVertical: (relaychain: Blockchain, parachain: Blockchain) => Promise<void>;
4
4
  export declare const connectParachains: (parachains: Blockchain[], disableAutoHrmp?: boolean) => Promise<void>;
@@ -87,6 +87,7 @@ const logger = defaultLogger.child({
87
87
  #registerBlock(block) {
88
88
  // if exceed max memory block count, delete the oldest block
89
89
  if (this.#blocksByNumber.size === this.#maxMemoryBlockCount) {
90
+ // #blocksByNumber can't be empty so the force unwrap is safe
90
91
  const { hash, number } = this.#blocksByNumber.values().next().value;
91
92
  this.#blocksByNumber.delete(number);
92
93
  this.#blocksByHash.delete(hash);
@@ -6,11 +6,11 @@ const logger = defaultLogger.child({
6
6
  name: 'layer'
7
7
  });
8
8
  const BATCH_SIZE = 1000;
9
- export var StorageValueKind;
10
- (function(StorageValueKind) {
9
+ export var StorageValueKind = /*#__PURE__*/ function(StorageValueKind) {
11
10
  StorageValueKind["Deleted"] = "Deleted";
12
11
  StorageValueKind["DeletedPrefix"] = "DeletedPrefix";
13
- })(StorageValueKind || (StorageValueKind = {}));
12
+ return StorageValueKind;
13
+ }({});
14
14
  export class RemoteStorageLayer {
15
15
  #api;
16
16
  #at;
@@ -8,12 +8,12 @@ const logger = defaultLogger.child({
8
8
  name: 'txpool'
9
9
  });
10
10
  export const APPLY_EXTRINSIC_ERROR = 'TxPool::ApplyExtrinsicError';
11
- export var BuildBlockMode;
12
- (function(BuildBlockMode) {
11
+ export var BuildBlockMode = /*#__PURE__*/ function(BuildBlockMode) {
13
12
  /** One block per batch (default) */ BuildBlockMode["Batch"] = "Batch";
14
13
  /** One block per tx */ BuildBlockMode["Instant"] = "Instant";
15
14
  /** Only build when triggered */ BuildBlockMode["Manual"] = "Manual";
16
- })(BuildBlockMode || (BuildBlockMode = {}));
15
+ return BuildBlockMode;
16
+ }({});
17
17
  export class TxPool {
18
18
  #chain;
19
19
  #pool = [];
@@ -1,4 +1,4 @@
1
1
  import { pino } from 'pino';
2
- export declare const pinoLogger: import("pino").Logger<never>;
3
- export declare const defaultLogger: pino.Logger<never>;
2
+ export declare const pinoLogger: import("pino").Logger<never, boolean>;
3
+ export declare const defaultLogger: pino.Logger<never, boolean>;
4
4
  export declare const truncate: (val: any) => any;
@@ -73,6 +73,12 @@ export type LimitReached = {
73
73
  * @return OperationStarted event with operationId to receive the result on the follow subscription
74
74
  */
75
75
  export declare const chainHead_v1_body: Handler<[string, HexString], OperationStarted | LimitReached>;
76
+ /**
77
+ * Resume an operation paused through `operationWaitingForContinue`
78
+ *
79
+ * @param context
80
+ * @param params - [`followSubscription`, `operationId`]
81
+ */
76
82
  export declare const chainHead_v1_continue: Handler<[string, HexString], null>;
77
83
  export declare const chainHead_v1_stopOperation: Handler<[string, HexString], null>;
78
84
  export declare const chainHead_v1_unpin: Handler<[string, HexString | HexString[]], null>;
@@ -3,7 +3,7 @@ import { defaultLogger } from '../../logger.js';
3
3
  const logger = defaultLogger.child({
4
4
  name: 'rpc-chainHead_v1'
5
5
  });
6
- const callbacks = new Map();
6
+ const following = new Map();
7
7
  async function afterResponse(fn) {
8
8
  await new Promise((resolve)=>setTimeout(resolve, 0));
9
9
  fn();
@@ -51,10 +51,13 @@ async function afterResponse(fn) {
51
51
  const id = context.chain.headState.subscribeHead(update);
52
52
  const cleanup = ()=>{
53
53
  context.chain.headState.unsubscribeHead(id);
54
- callbacks.delete(id);
54
+ following.delete(id);
55
55
  };
56
56
  const callback = subscribe('chainHead_v1_followEvent', id, cleanup);
57
- callbacks.set(id, callback);
57
+ following.set(id, {
58
+ callback,
59
+ pendingDescendantValues: new Map()
60
+ });
58
61
  afterResponse(async ()=>{
59
62
  callback({
60
63
  event: 'initialized',
@@ -84,7 +87,7 @@ async function afterResponse(fn) {
84
87
  *
85
88
  * @return SCALE-encoded header, or null if the block is not found.
86
89
  */ export const chainHead_v1_header = async (context, [followSubscription, hash])=>{
87
- if (!callbacks.has(followSubscription)) return null;
90
+ if (!following.has(followSubscription)) return null;
88
91
  const block = await context.chain.getBlock(hash);
89
92
  return block ? (await block.header).toHex() : null;
90
93
  };
@@ -105,7 +108,7 @@ const randomId = ()=>Math.random().toString(36).substring(2);
105
108
  afterResponse(async ()=>{
106
109
  const block = await context.chain.getBlock(hash);
107
110
  if (!block) {
108
- callbacks.get(followSubscription)?.({
111
+ following.get(followSubscription)?.callback({
109
112
  event: 'operationError',
110
113
  operationId,
111
114
  error: `Block ${hash} not found`
@@ -115,13 +118,13 @@ const randomId = ()=>Math.random().toString(36).substring(2);
115
118
  const resp = await block.call(method, [
116
119
  callParameters
117
120
  ]);
118
- callbacks.get(followSubscription)?.({
121
+ following.get(followSubscription)?.callback({
119
122
  event: 'operationCallDone',
120
123
  operationId,
121
124
  output: resp.result
122
125
  });
123
126
  } catch (ex) {
124
- callbacks.get(followSubscription)?.({
127
+ following.get(followSubscription)?.callback({
125
128
  event: 'operationError',
126
129
  operationId,
127
130
  error: ex.message
@@ -131,6 +134,30 @@ const randomId = ()=>Math.random().toString(36).substring(2);
131
134
  });
132
135
  return operationStarted(operationId);
133
136
  };
137
+ const PAGE_SIZE = 1000;
138
+ async function getDescendantValues(block, params) {
139
+ const keys = await block.getKeysPaged({
140
+ ...params,
141
+ pageSize: PAGE_SIZE
142
+ });
143
+ const items = await Promise.all(keys.map((key)=>block.get(key).then((value)=>({
144
+ key,
145
+ value
146
+ }))));
147
+ if (keys.length < PAGE_SIZE) {
148
+ return {
149
+ items,
150
+ next: null
151
+ };
152
+ }
153
+ return {
154
+ items,
155
+ next: {
156
+ ...params,
157
+ startKey: keys[PAGE_SIZE - 1]
158
+ }
159
+ };
160
+ }
134
161
  /**
135
162
  * Query the storage for a given block
136
163
  *
@@ -143,7 +170,7 @@ const randomId = ()=>Math.random().toString(36).substring(2);
143
170
  afterResponse(async ()=>{
144
171
  const block = await context.chain.getBlock(hash);
145
172
  if (!block) {
146
- callbacks.get(followSubscription)?.({
173
+ following.get(followSubscription)?.callback({
147
174
  event: 'operationError',
148
175
  operationId,
149
176
  error: 'Block not found'
@@ -156,7 +183,7 @@ const randomId = ()=>Math.random().toString(36).substring(2);
156
183
  {
157
184
  const value = await block.get(sir.key);
158
185
  if (value) {
159
- callbacks.get(followSubscription)?.({
186
+ following.get(followSubscription)?.callback({
160
187
  event: 'operationStorageItems',
161
188
  operationId,
162
189
  items: [
@@ -167,43 +194,47 @@ const randomId = ()=>Math.random().toString(36).substring(2);
167
194
  ]
168
195
  });
169
196
  }
170
- break;
197
+ return null;
171
198
  }
172
199
  case 'descendantsValues':
173
200
  {
174
- // TODO expose pagination
175
- const pageSize = 100;
176
- let startKey = '0x';
177
- while(startKey){
178
- const keys = await block.getKeysPaged({
179
- prefix: sir.key,
180
- pageSize,
181
- startKey
182
- });
183
- startKey = keys[pageSize - 1] ?? null;
184
- const items = await Promise.all(keys.map((key)=>block.get(key).then((value)=>({
185
- key,
186
- value
187
- }))));
188
- callbacks.get(followSubscription)?.({
189
- event: 'operationStorageItems',
190
- operationId,
191
- items
192
- });
193
- break;
194
- }
195
- break;
201
+ const { items, next } = await getDescendantValues(block, {
202
+ prefix: sir.key,
203
+ startKey: '0x'
204
+ });
205
+ following.get(followSubscription)?.callback({
206
+ event: 'operationStorageItems',
207
+ operationId,
208
+ items
209
+ });
210
+ return next;
196
211
  }
197
212
  default:
198
213
  // TODO
199
214
  console.warn(`Storage type not implemented ${sir.type}`);
215
+ return null;
200
216
  }
201
217
  };
202
- await Promise.all(items.map(handleStorageItemRequest));
203
- callbacks.get(followSubscription)?.({
204
- event: 'operationStorageDone',
205
- operationId
206
- });
218
+ const listResult = await Promise.all(items.map(handleStorageItemRequest));
219
+ const pending = listResult.filter((v)=>v !== null);
220
+ if (!pending.length) {
221
+ following.get(followSubscription)?.callback({
222
+ event: 'operationStorageDone',
223
+ operationId
224
+ });
225
+ } else {
226
+ const follower = following.get(followSubscription);
227
+ if (follower) {
228
+ follower.pendingDescendantValues.set(operationId, {
229
+ hash,
230
+ params: pending
231
+ });
232
+ follower.callback({
233
+ event: 'operationWaitingForContinue',
234
+ operationId
235
+ });
236
+ }
237
+ }
207
238
  });
208
239
  return {
209
240
  ...operationStarted(operationId),
@@ -221,7 +252,7 @@ const limitReached = {
221
252
  *
222
253
  * @return OperationStarted event with operationId to receive the result on the follow subscription
223
254
  */ export const chainHead_v1_body = async (context, [followSubscription, hash])=>{
224
- if (!callbacks.has(followSubscription)) return limitReached;
255
+ if (!following.has(followSubscription)) return limitReached;
225
256
  const block = await context.chain.getBlock(hash);
226
257
  if (!block) {
227
258
  throw new ResponseError(-32801, 'Block not found');
@@ -229,7 +260,7 @@ const limitReached = {
229
260
  const operationId = randomId();
230
261
  afterResponse(async ()=>{
231
262
  const body = await block.extrinsics;
232
- callbacks.get(followSubscription)?.({
263
+ following.get(followSubscription)?.callback({
233
264
  event: 'operationBodyDone',
234
265
  operationId,
235
266
  value: body
@@ -237,11 +268,54 @@ const limitReached = {
237
268
  });
238
269
  return operationStarted(operationId);
239
270
  };
240
- // Currently no-ops, will come into play when pagination is implemented
241
- export const chainHead_v1_continue = async (_context, [_followSubscription, _operationId])=>{
271
+ /**
272
+ * Resume an operation paused through `operationWaitingForContinue`
273
+ *
274
+ * @param context
275
+ * @param params - [`followSubscription`, `operationId`]
276
+ */ export const chainHead_v1_continue = async (context, [followSubscription, operationId])=>{
277
+ const follower = following.get(followSubscription);
278
+ const pendingOp = follower?.pendingDescendantValues.get(operationId);
279
+ if (!pendingOp || !follower) {
280
+ throw new ResponseError(-32803, "Operation ID doesn't have anything pending");
281
+ }
282
+ const block = await context.chain.getBlock(pendingOp.hash);
283
+ if (!block) {
284
+ throw new ResponseError(-32801, 'Block not found');
285
+ }
286
+ afterResponse(async ()=>{
287
+ const handlePendingOperation = async (params)=>{
288
+ const { items, next } = await getDescendantValues(block, params);
289
+ follower.callback({
290
+ event: 'operationStorageItems',
291
+ operationId,
292
+ items
293
+ });
294
+ return next;
295
+ };
296
+ const listResult = await Promise.all(pendingOp.params.map(handlePendingOperation));
297
+ const pending = listResult.filter((v)=>v !== null);
298
+ if (!pending.length) {
299
+ follower.pendingDescendantValues.delete(operationId);
300
+ follower.callback({
301
+ event: 'operationStorageDone',
302
+ operationId
303
+ });
304
+ } else {
305
+ follower.pendingDescendantValues.set(operationId, {
306
+ hash: pendingOp.hash,
307
+ params: pending
308
+ });
309
+ follower.callback({
310
+ event: 'operationWaitingForContinue',
311
+ operationId
312
+ });
313
+ }
314
+ });
242
315
  return null;
243
316
  };
244
- export const chainHead_v1_stopOperation = async (_context, [_followSubscription, _operationId])=>{
317
+ export const chainHead_v1_stopOperation = async (_context, [followSubscription, operationId])=>{
318
+ following.get(followSubscription)?.pendingDescendantValues.delete(operationId);
245
319
  return null;
246
320
  };
247
321
  // no-op, since there's no concept of unpinning in chopsticks
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { Blockchain } from '../blockchain/index.js';
3
- export declare const logger: import("pino").default.Logger<never>;
3
+ export declare const logger: import("pino").default.Logger<never, boolean>;
4
4
  export declare const zHex: z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>;
5
5
  export declare const zHash: z.ZodIntersection<z.ZodString, z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>>;
6
6
  export declare class ResponseError extends Error {
@@ -41,7 +41,7 @@ export const isUrl = (url)=>{
41
41
  try {
42
42
  new URL(url);
43
43
  return true;
44
- } catch (e) {
44
+ } catch (_e) {
45
45
  return false;
46
46
  }
47
47
  };
@@ -1,4 +1,4 @@
1
1
  import { Blockchain } from '../blockchain/index.js';
2
- export declare const xcmLogger: import("pino").default.Logger<never>;
2
+ export declare const xcmLogger: import("pino").default.Logger<never, boolean>;
3
3
  export declare const connectVertical: (relaychain: Blockchain, parachain: Blockchain) => Promise<void>;
4
4
  export declare const connectParachains: (parachains: Blockchain[], disableAutoHrmp?: boolean) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acala-network/chopsticks-core",
3
- "version": "0.16.2",
3
+ "version": "1.0.0",
4
4
  "author": "Acala Developers <hello@acala.network>",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -11,28 +11,28 @@
11
11
  "depcheck": "npx depcheck"
12
12
  },
13
13
  "dependencies": {
14
- "@acala-network/chopsticks-executor": "0.16.2",
15
- "@polkadot/rpc-provider": "^12.3.1",
16
- "@polkadot/types": "^12.3.1",
17
- "@polkadot/types-codec": "^12.3.1",
18
- "@polkadot/types-known": "^12.3.1",
19
- "@polkadot/util": "^13.0.2",
20
- "@polkadot/util-crypto": "^13.0.2",
14
+ "@acala-network/chopsticks-executor": "1.0.0",
15
+ "@polkadot/rpc-provider": "^14.0.1",
16
+ "@polkadot/types": "^14.0.1",
17
+ "@polkadot/types-codec": "^14.0.1",
18
+ "@polkadot/types-known": "^14.0.1",
19
+ "@polkadot/util": "^13.2.2",
20
+ "@polkadot/util-crypto": "^13.2.2",
21
21
  "comlink": "^4.4.1",
22
22
  "eventemitter3": "^5.0.1",
23
23
  "lodash": "^4.17.21",
24
- "lru-cache": "^10.2.0",
25
- "pino": "^8.19.0",
26
- "pino-pretty": "^11.0.0",
24
+ "lru-cache": "^11.0.1",
25
+ "pino": "^9.5.0",
26
+ "pino-pretty": "^11.3.0",
27
27
  "rxjs": "^7.8.1",
28
- "zod": "^3.22.4"
28
+ "zod": "^3.23.8"
29
29
  },
30
30
  "devDependencies": {
31
- "@swc/cli": "0.4.0",
32
- "@swc/core": "^1.7.6",
33
- "@types/lodash": "^4.17.6",
34
- "typescript": "^5.5.3",
35
- "vitest": "^1.4.0"
31
+ "@swc/cli": "0.5.0",
32
+ "@swc/core": "^1.7.40",
33
+ "@types/lodash": "^4.17.13",
34
+ "typescript": "^5.6.3",
35
+ "vitest": "^2.1.4"
36
36
  },
37
37
  "files": [
38
38
  "dist/esm/**",