@d9-network/ink 0.0.4 → 0.0.6

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/dist/index.mjs CHANGED
@@ -207,19 +207,19 @@ function buildCodecFromType(typeId, types, cache) {
207
207
  const def = typeEntry.type.def;
208
208
  const path = typeEntry.type.path;
209
209
  if (def.primitive) {
210
- const codec = buildPrimitiveCodec(def.primitive);
210
+ const codec = buildPrimitiveCodec$1(def.primitive);
211
211
  cache.set(typeId, codec);
212
212
  return codec;
213
213
  }
214
214
  if (path && path.length > 0) {
215
- const specialCodec = buildSpecialTypeCodec(path, typeEntry, types, cache);
215
+ const specialCodec = buildSpecialTypeCodec$1(path, typeEntry, types, cache);
216
216
  if (specialCodec) {
217
217
  cache.set(typeId, specialCodec);
218
218
  return specialCodec;
219
219
  }
220
220
  }
221
221
  if (def.tuple) {
222
- const codec = buildTupleCodec(def.tuple, types, cache);
222
+ const codec = buildTupleCodec$1(def.tuple, types, cache);
223
223
  cache.set(typeId, codec);
224
224
  return codec;
225
225
  }
@@ -230,7 +230,7 @@ function buildCodecFromType(typeId, types, cache) {
230
230
  }
231
231
  if (def.array) {
232
232
  const innerCodec = buildCodecFromType(def.array.type, types, cache);
233
- if (def.array.type === findPrimitiveTypeId(types, "u8")) {
233
+ if (def.array.type === findPrimitiveTypeId$1(types, "u8")) {
234
234
  const codec$1 = Bytes$1(def.array.len);
235
235
  cache.set(typeId, codec$1);
236
236
  return codec$1;
@@ -240,12 +240,12 @@ function buildCodecFromType(typeId, types, cache) {
240
240
  return codec;
241
241
  }
242
242
  if (def.composite) {
243
- const codec = buildCompositeCodec(def.composite, types, cache);
243
+ const codec = buildCompositeCodec$1(def.composite, types, cache);
244
244
  cache.set(typeId, codec);
245
245
  return codec;
246
246
  }
247
247
  if (def.variant) {
248
- const codec = buildVariantCodec(def.variant, path, types, cache);
248
+ const codec = buildVariantCodec$1(def.variant, path, types, cache);
249
249
  cache.set(typeId, codec);
250
250
  return codec;
251
251
  }
@@ -254,7 +254,7 @@ function buildCodecFromType(typeId, types, cache) {
254
254
  /**
255
255
  * Build codec for primitive types
256
256
  */
257
- function buildPrimitiveCodec(primitive) {
257
+ function buildPrimitiveCodec$1(primitive) {
258
258
  switch (primitive) {
259
259
  case "u8": return u8;
260
260
  case "u16": return u16;
@@ -274,7 +274,7 @@ function buildPrimitiveCodec(primitive) {
274
274
  /**
275
275
  * Build codec for special types based on path
276
276
  */
277
- function buildSpecialTypeCodec(path, typeEntry, types, cache) {
277
+ function buildSpecialTypeCodec$1(path, typeEntry, types, cache) {
278
278
  if (path.join("::").includes("AccountId")) return AccountId();
279
279
  if (path[0] === "Option") {
280
280
  const params = typeEntry.type.params;
@@ -296,7 +296,7 @@ function buildSpecialTypeCodec(path, typeEntry, types, cache) {
296
296
  /**
297
297
  * Build codec for tuple types
298
298
  */
299
- function buildTupleCodec(tupleTypes, types, cache) {
299
+ function buildTupleCodec$1(tupleTypes, types, cache) {
300
300
  if (tupleTypes.length === 0) return _void;
301
301
  const innerCodecs = tupleTypes.map((t) => buildCodecFromType(t, types, cache));
302
302
  switch (innerCodecs.length) {
@@ -310,7 +310,7 @@ function buildTupleCodec(tupleTypes, types, cache) {
310
310
  /**
311
311
  * Build codec for composite (struct) types
312
312
  */
313
- function buildCompositeCodec(composite, types, cache) {
313
+ function buildCompositeCodec$1(composite, types, cache) {
314
314
  const fields = composite.fields;
315
315
  if (fields.length === 1 && !fields[0]?.name) return buildCodecFromType(fields[0].type, types, cache);
316
316
  const structDef = {};
@@ -323,7 +323,7 @@ function buildCompositeCodec(composite, types, cache) {
323
323
  /**
324
324
  * Build codec for variant (enum) types
325
325
  */
326
- function buildVariantCodec(variant, path, types, cache) {
326
+ function buildVariantCodec$1(variant, path, types, cache) {
327
327
  const variants = variant.variants;
328
328
  const isLangError$1 = path?.includes("LangError");
329
329
  const variantDef = {};
@@ -353,7 +353,7 @@ function buildVariantCodec(variant, path, types, cache) {
353
353
  /**
354
354
  * Find the type ID for a primitive type
355
355
  */
356
- function findPrimitiveTypeId(types, primitive) {
356
+ function findPrimitiveTypeId$1(types, primitive) {
357
357
  return types.find((t) => t.type.def.primitive === primitive)?.id ?? -1;
358
358
  }
359
359
  /**
@@ -470,7 +470,12 @@ function getEventSignature(eventLabel) {
470
470
  //#endregion
471
471
  //#region src/events.ts
472
472
  /**
473
- * Event parser for a specific contract
473
+ * Type-safe event parser for a specific contract
474
+ *
475
+ * @typeParam S - The storage descriptor type
476
+ * @typeParam M - The messages descriptor type
477
+ * @typeParam C - The constructors descriptor type
478
+ * @typeParam E - The event type representing all possible events for this contract
474
479
  */
475
480
  var ContractEventParser = class {
476
481
  eventDecoders;
@@ -478,21 +483,31 @@ var ContractEventParser = class {
478
483
  contractAddressBytes;
479
484
  contractAddress;
480
485
  metadata;
481
- constructor(metadata, contractAddress) {
482
- this.metadata = metadata;
486
+ constructor(descriptor, contractAddress) {
487
+ if (!descriptor.metadata) throw new Error("Contract descriptor must include metadata");
488
+ this.metadata = descriptor.metadata;
483
489
  this.contractAddress = contractAddress;
484
- this.eventDecoders = buildAllEventDecoders(metadata);
490
+ this.eventDecoders = buildAllEventDecoders(this.metadata);
485
491
  this.contractAddressBytes = ss58Decode(contractAddress)[0];
486
492
  this.eventSignatures = /* @__PURE__ */ new Map();
487
- const events = metadata.spec.events;
493
+ const events = this.metadata.spec.events;
488
494
  for (const event of events) {
489
495
  const sig = getEventSignature(event.label);
490
496
  this.eventSignatures.set(event.label, sig);
491
497
  }
492
498
  }
493
499
  /**
494
- * Parse a raw chain event into a contract event (if it matches)
500
+ * Parse a raw chain event into a type-safe contract event (if it matches)
495
501
  * Returns null if the event is not from this contract or cannot be parsed
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * const event = parser.parseEvent(chainEvent);
506
+ * if (event?.type === "Transfer") {
507
+ * // event.value is now typed as { from: SS58String; to: SS58String; value: bigint }
508
+ * console.log(event.value.from);
509
+ * }
510
+ * ```
496
511
  */
497
512
  parseEvent(chainEvent) {
498
513
  const extracted = this.extractContractEmittedEvent(chainEvent);
@@ -518,8 +533,8 @@ var ContractEventParser = class {
518
533
  try {
519
534
  const decodedData = decoder(data);
520
535
  return {
521
- label: eventLabel,
522
- data: decodedData,
536
+ type: eventLabel,
537
+ value: decodedData,
523
538
  raw: {
524
539
  blockNumber,
525
540
  blockHash,
@@ -535,14 +550,18 @@ var ContractEventParser = class {
535
550
  }
536
551
  }
537
552
  /**
538
- * Filter a batch of events
553
+ * Filter a batch of events and return type-safe results
554
+ *
555
+ * @param chainEvents - Array of chain events to filter
556
+ * @param options - Optional filter criteria
557
+ * @returns Array of type-safe contract events
539
558
  */
540
559
  filterEvents(chainEvents, options) {
541
560
  const results = [];
542
561
  for (const chainEvent of chainEvents) {
543
562
  const parsed = this.parseEvent(chainEvent);
544
563
  if (!parsed) continue;
545
- if (options?.eventLabels && !options.eventLabels.includes(parsed.label)) continue;
564
+ if (options?.eventLabels && !options.eventLabels.includes(parsed.type)) continue;
546
565
  if (options?.fromBlock && parsed.raw.blockNumber < options.fromBlock) continue;
547
566
  if (options?.toBlock && parsed.raw.blockNumber > options.toBlock) continue;
548
567
  results.push(parsed);
@@ -550,6 +569,29 @@ var ContractEventParser = class {
550
569
  return results;
551
570
  }
552
571
  /**
572
+ * Filter events by specific type with proper type narrowing
573
+ *
574
+ * This method provides better type safety than filterEvents with eventLabels
575
+ * because TypeScript can narrow the return type to only the specified event type.
576
+ *
577
+ * @param chainEvents - Array of chain events to filter
578
+ * @param label - The event label to filter by
579
+ * @returns Array of events narrowed to the specific type
580
+ *
581
+ * @example
582
+ * ```ts
583
+ * const transfers = parser.filterByType(events, "Transfer");
584
+ *
585
+ * for (const t of transfers) {
586
+ * // t.value is fully typed as Transfer event data
587
+ * console.log(t.value.from, "->", t.value.to, ":", t.value.value);
588
+ * }
589
+ * ```
590
+ */
591
+ filterByType(chainEvents, label) {
592
+ return this.filterEvents(chainEvents).filter((e) => e.type === label);
593
+ }
594
+ /**
553
595
  * Get the contract address as SS58 string
554
596
  */
555
597
  getContractAddress() {
@@ -570,20 +612,31 @@ var ContractEventParser = class {
570
612
  if (eventValue.type !== "ContractEmitted") return null;
571
613
  const contractEmittedData = eventValue.value;
572
614
  if (!contractEmittedData || typeof contractEmittedData !== "object") return null;
573
- const contract = contractEmittedData.contract;
574
- const data = contractEmittedData.data;
575
- if (!(contract instanceof Uint8Array) || !(data instanceof Uint8Array)) return null;
615
+ const contractRaw = contractEmittedData.contract;
616
+ const dataRaw = contractEmittedData.data;
617
+ let contract;
618
+ if (contractRaw instanceof Uint8Array) contract = contractRaw;
619
+ else if (typeof contractRaw === "string") contract = ss58Decode(contractRaw)[0];
620
+ else return null;
621
+ let data;
622
+ if (dataRaw instanceof Uint8Array) data = dataRaw;
623
+ else if (dataRaw && typeof dataRaw === "object" && "asBytes" in dataRaw && typeof dataRaw.asBytes === "function") data = dataRaw.asBytes();
624
+ else return null;
576
625
  const topics = [];
577
626
  if (record.topics && Array.isArray(record.topics)) {
578
627
  for (const topic of record.topics) if (topic instanceof Uint8Array) topics.push(topic);
628
+ else if (topic && typeof topic === "object" && "asBytes" in topic && typeof topic.asBytes === "function") topics.push(topic.asBytes());
579
629
  }
630
+ const blockNumber = typeof record.blockNumber === "number" ? record.blockNumber : 0;
631
+ const blockHash = typeof record.blockHash === "string" ? record.blockHash : "";
632
+ const eventIndex = typeof record.eventIndex === "number" ? record.eventIndex : 0;
580
633
  return {
581
634
  contract,
582
635
  data,
583
636
  topics,
584
- blockNumber: typeof record.blockNumber === "number" ? record.blockNumber : 0,
585
- blockHash: typeof record.blockHash === "string" ? record.blockHash : "",
586
- eventIndex: typeof record.eventIndex === "number" ? record.eventIndex : 0
637
+ blockNumber,
638
+ blockHash,
639
+ eventIndex
587
640
  };
588
641
  }
589
642
  /**
@@ -607,6 +660,34 @@ var ContractEventParser = class {
607
660
  return signatures;
608
661
  }
609
662
  };
663
+ /**
664
+ * Type guard for narrowing event types
665
+ *
666
+ * Use this function when you have a `TypedContractEvent` and need to
667
+ * narrow it to a specific event type. This is useful when the type
668
+ * information is lost (e.g., after serialization or in generic functions).
669
+ *
670
+ * @typeParam E - The Enum type representing all possible events
671
+ * @typeParam L - The specific event label to check
672
+ * @param event - The event to check
673
+ * @param label - The event label to match
674
+ * @returns True if the event type matches the label
675
+ *
676
+ * @example
677
+ * ```ts
678
+ * const event = parser.parseEvent(chainEvent);
679
+ *
680
+ * if (event && isEventType(event, "Transfer")) {
681
+ * // TypeScript knows event.value is Transfer event data
682
+ * console.log(event.value.from);
683
+ * console.log(event.value.to);
684
+ * console.log(event.value.value);
685
+ * }
686
+ * ```
687
+ */
688
+ function isEventType(event, label) {
689
+ return event.type === label;
690
+ }
610
691
 
611
692
  //#endregion
612
693
  //#region src/subscriptions.ts
@@ -617,12 +698,12 @@ var ContractEventParser = class {
617
698
  * Create an observable stream of contract events
618
699
  *
619
700
  * @param client - Polkadot API client
620
- * @param metadata - Contract metadata
701
+ * @param descriptor - Contract descriptor
621
702
  * @param options - Subscription options
622
- * @returns Observable stream of decoded contract events
703
+ * @returns Observable stream of type-safe contract events
623
704
  */
624
- function createContractEventStream(client, metadata, options) {
625
- const parser = new ContractEventParser(metadata, options.contractAddress);
705
+ function createContractEventStream(client, descriptor, options) {
706
+ const parser = new ContractEventParser(descriptor, options.contractAddress);
626
707
  return client.finalizedBlock$.pipe(mergeMap(async (block) => {
627
708
  try {
628
709
  return {
@@ -648,7 +729,7 @@ function createContractEventStream(client, metadata, options) {
648
729
  }).filter((e) => e !== null);
649
730
  }), mergeMap((events) => from(events)), filter((event) => {
650
731
  if (!options.eventLabels) return true;
651
- return options.eventLabels.includes(event.label);
732
+ return options.eventLabels.includes(event.type);
652
733
  }), catchError((error) => {
653
734
  console.error("Error in contract event stream:", error);
654
735
  return of();
@@ -658,21 +739,21 @@ function createContractEventStream(client, metadata, options) {
658
739
  * Convenience helper to create a Transfer event stream for PSP22 tokens
659
740
  *
660
741
  * @param client - Polkadot API client
661
- * @param metadata - PSP22 contract metadata
742
+ * @param descriptor - PSP22 contract descriptor
662
743
  * @param contractAddress - PSP22 contract address
663
744
  * @param getEvents - Function to fetch System.Events at a block hash
664
745
  * @param watchAddress - Optional address to filter transfers (only events involving this address)
665
746
  * @returns Observable stream of Transfer events
666
747
  */
667
- function createPSP22TransferStream(client, metadata, contractAddress, getEvents, watchAddress) {
668
- return createContractEventStream(client, metadata, {
748
+ function createPSP22TransferStream(client, descriptor, contractAddress, getEvents, watchAddress) {
749
+ return createContractEventStream(client, descriptor, {
669
750
  contractAddress,
670
751
  eventLabels: ["Transfer"],
671
752
  getEvents
672
753
  }).pipe(filter((event) => {
673
754
  if (!watchAddress) return true;
674
- const data = event.data;
675
- return data.from === watchAddress || data.to === watchAddress;
755
+ const value = event.value;
756
+ return value.from === watchAddress || value.to === watchAddress;
676
757
  }));
677
758
  }
678
759
  /**
@@ -719,6 +800,91 @@ function createNativeTransferStream(client, getEvents, watchAddress) {
719
800
  }), share());
720
801
  }
721
802
 
803
+ //#endregion
804
+ //#region src/message-builder.ts
805
+ /**
806
+ * Create a type-safe message builder from a contract descriptor
807
+ *
808
+ * @typeParam S - Storage descriptor type
809
+ * @typeParam M - Messages descriptor type
810
+ * @typeParam C - Constructors descriptor type
811
+ * @typeParam E - Events Enum type
812
+ * @param descriptor - The ink! contract descriptor containing metadata
813
+ * @returns A ContractMessageBuilder instance
814
+ *
815
+ * @example
816
+ * ```ts
817
+ * import { createMessageBuilder } from '@d9-network/ink';
818
+ * import { contracts } from '@polkadot-api/descriptors';
819
+ *
820
+ * const builder = createMessageBuilder(contracts.usdt);
821
+ *
822
+ * // Get a typed message interface
823
+ * const transfer = builder.message("PSP22::transfer");
824
+ *
825
+ * // Encode with full type checking on args
826
+ * const encoded = transfer.encode({
827
+ * to: recipientAddress, // Must be SS58String
828
+ * value: 1000000n, // Must be bigint
829
+ * data: new Uint8Array(), // Must be Uint8Array
830
+ * });
831
+ *
832
+ * // Decode response with full type inference
833
+ * const response = transfer.decode(resultBytes);
834
+ * ```
835
+ */
836
+ function createMessageBuilder(descriptor) {
837
+ if (!descriptor.metadata) throw new Error("Contract descriptor must include metadata");
838
+ const metadata = descriptor.metadata;
839
+ const builder = getInkDynamicBuilder(getInkLookup(metadata));
840
+ const codecCache = /* @__PURE__ */ new Map();
841
+ const messagesMetadata = /* @__PURE__ */ new Map();
842
+ for (const msg of metadata.spec.messages) messagesMetadata.set(msg.label, msg);
843
+ function getMessageCodec(label) {
844
+ const cached = codecCache.get(label);
845
+ if (cached) return cached;
846
+ const codec = builder.buildMessage(label);
847
+ codecCache.set(label, codec);
848
+ return codec;
849
+ }
850
+ function parseSelector(selectorHex) {
851
+ const hex = selectorHex.startsWith("0x") ? selectorHex.slice(2) : selectorHex;
852
+ return new Uint8Array(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
853
+ }
854
+ function message(label) {
855
+ const msgMeta = messagesMetadata.get(label);
856
+ if (!msgMeta) throw new Error(`Message "${label}" not found in metadata`);
857
+ const codec = getMessageCodec(label);
858
+ const selector = parseSelector(msgMeta.selector);
859
+ const encode = (args) => {
860
+ const encoded = codec.call.enc(args ?? {});
861
+ return Binary.fromBytes(encoded);
862
+ };
863
+ const decode = (response) => {
864
+ const bytes = response instanceof Uint8Array ? response : response.asBytes();
865
+ return codec.value.dec(bytes);
866
+ };
867
+ return {
868
+ encode,
869
+ decode,
870
+ attributes: {
871
+ mutates: msgMeta.mutates,
872
+ payable: msgMeta.payable,
873
+ default: msgMeta.default
874
+ },
875
+ selector,
876
+ label
877
+ };
878
+ }
879
+ function getMessageLabels() {
880
+ return Array.from(messagesMetadata.keys());
881
+ }
882
+ return {
883
+ message,
884
+ getMessageLabels
885
+ };
886
+ }
887
+
722
888
  //#endregion
723
889
  //#region src/errors.ts
724
890
  /**
@@ -937,6 +1103,10 @@ function createD9InkContract(descriptor, address, options) {
937
1103
  if (!descriptor.metadata) throw new MetadataError("Contract descriptor must include metadata");
938
1104
  const patchedMetadata = patchLangErrorInMetadata(descriptor.metadata);
939
1105
  const builder = getInkDynamicBuilder(getInkLookup(patchedMetadata));
1106
+ const patchedDescriptor = {
1107
+ ...descriptor,
1108
+ metadata: patchedMetadata
1109
+ };
940
1110
  let codecRegistry;
941
1111
  try {
942
1112
  codecRegistry = createCodecRegistry(patchedMetadata);
@@ -971,13 +1141,13 @@ function createD9InkContract(descriptor, address, options) {
971
1141
  checkAborted(signal, method);
972
1142
  const originBytes = ss58ToBytes(origin);
973
1143
  const codec = getMessageCodec(method);
974
- const message = encodeContractCall(originBytes, addressBytes, codec.call.enc(args ?? {}), value);
1144
+ const message$1 = encodeContractCall(originBytes, addressBytes, codec.call.enc(args ?? {}), value);
975
1145
  const blockHash = at ?? (await client.getFinalizedBlock()).hash;
976
1146
  checkAborted(signal, method);
977
1147
  const executeCall = async () => {
978
1148
  return fromHex(await client._request("state_call", [
979
1149
  "ContractsApi_call",
980
- message,
1150
+ message$1,
981
1151
  blockHash
982
1152
  ]));
983
1153
  };
@@ -1079,11 +1249,11 @@ function createD9InkContract(descriptor, address, options) {
1079
1249
  try {
1080
1250
  let gas = gasLimit;
1081
1251
  if (!gas) {
1082
- const message = encodeContractCall(originBytes, addressBytes, callData, value);
1252
+ const message$1 = encodeContractCall(originBytes, addressBytes, callData, value);
1083
1253
  const blockHash = (await client.getFinalizedBlock()).hash;
1084
1254
  gas = decodeContractCallResult(fromHex(await client._request("state_call", [
1085
1255
  "ContractsApi_call",
1086
- message,
1256
+ message$1,
1087
1257
  blockHash
1088
1258
  ]))).gas.gasRequired;
1089
1259
  }
@@ -1152,7 +1322,20 @@ function createD9InkContract(descriptor, address, options) {
1152
1322
  * Filter events for this contract
1153
1323
  */
1154
1324
  function filterEvents(events) {
1155
- return new ContractEventParser(patchedMetadata, address).filterEvents(events);
1325
+ return new ContractEventParser(patchedDescriptor, address).filterEvents(events);
1326
+ }
1327
+ /**
1328
+ * Filter events by specific type with proper type narrowing
1329
+ */
1330
+ function filterEventsByType(events, label) {
1331
+ return new ContractEventParser(patchedDescriptor, address).filterByType(events, label);
1332
+ }
1333
+ const messageBuilder = createMessageBuilder(descriptor);
1334
+ /**
1335
+ * Get a type-safe message interface
1336
+ */
1337
+ function message(label) {
1338
+ return messageBuilder.message(label);
1156
1339
  }
1157
1340
  /**
1158
1341
  * Subscribe to contract events as an RxJS Observable
@@ -1163,7 +1346,7 @@ function createD9InkContract(descriptor, address, options) {
1163
1346
  * @param options.fromBlock - Optional starting block number
1164
1347
  */
1165
1348
  function subscribeToEvents(options$1) {
1166
- return createContractEventStream(client, patchedMetadata, {
1349
+ return createContractEventStream(client, patchedDescriptor, {
1167
1350
  ...options$1,
1168
1351
  contractAddress: address
1169
1352
  });
@@ -1175,10 +1358,43 @@ function createD9InkContract(descriptor, address, options) {
1175
1358
  send,
1176
1359
  getStorage,
1177
1360
  filterEvents,
1361
+ filterEventsByType,
1362
+ message,
1178
1363
  subscribeToEvents
1179
1364
  };
1180
1365
  }
1181
1366
 
1367
+ //#endregion
1368
+ //#region src/rpc.ts
1369
+ /**
1370
+ * Create a type-safe RPC request function from a PolkadotClient
1371
+ *
1372
+ * This wraps the client's `_request` method with proper TypeScript types,
1373
+ * providing autocomplete for known RPC methods and type inference for
1374
+ * parameters and return values.
1375
+ *
1376
+ * @param client - The PolkadotClient instance
1377
+ * @returns A type-safe RPC request function
1378
+ *
1379
+ * @example
1380
+ * ```ts
1381
+ * const rpc = createTypedRpc(client);
1382
+ *
1383
+ * // Autocomplete for method names, typed params and return
1384
+ * const hash = await rpc("chain_getBlockHash", [12345]);
1385
+ * // hash: HexString | null
1386
+ *
1387
+ * const header = await rpc("chain_getHeader", [hash]);
1388
+ * // header: BlockHeader | null
1389
+ *
1390
+ * // Custom methods still work with explicit types
1391
+ * const custom = await rpc<MyType>("my_custom_method", [arg]);
1392
+ * ```
1393
+ */
1394
+ function createTypedRpc(client) {
1395
+ return ((method, params) => client._request(method, params));
1396
+ }
1397
+
1182
1398
  //#endregion
1183
1399
  //#region src/sdk.ts
1184
1400
  /**
@@ -1222,20 +1438,378 @@ function createD9InkContract(descriptor, address, options) {
1222
1438
  *
1223
1439
  * @param client - The PolkadotClient instance
1224
1440
  * @param options - Optional SDK configuration
1225
- * @returns D9 Ink SDK instance
1441
+ * @returns D9 Ink SDK instance with typed RPC access
1226
1442
  */
1227
1443
  function createD9InkSdk(client, options = {}) {
1228
1444
  const { typedApi, defaultQueryOptions, defaultSendOptions } = options;
1229
- return { getContract(descriptor, address) {
1230
- return createD9InkContract(descriptor, address, {
1231
- client,
1232
- typedApi,
1233
- defaultQueryOptions,
1234
- defaultSendOptions
1445
+ return {
1446
+ getContract(descriptor, address) {
1447
+ return createD9InkContract(descriptor, address, {
1448
+ client,
1449
+ typedApi,
1450
+ defaultQueryOptions,
1451
+ defaultSendOptions
1452
+ });
1453
+ },
1454
+ rpc: createTypedRpc(client)
1455
+ };
1456
+ }
1457
+
1458
+ //#endregion
1459
+ //#region src/codec-builder-internal.ts
1460
+ /**
1461
+ * Internal codec building utilities
1462
+ *
1463
+ * This module provides lower-level codec building from metadata type IDs.
1464
+ */
1465
+ /**
1466
+ * Build a SCALE codec from ink metadata type definition
1467
+ */
1468
+ function buildCodecFromTypeInternal(typeId, types, cache) {
1469
+ const cached = cache.get(typeId);
1470
+ if (cached) return cached;
1471
+ const typeEntry = types.find((t) => t.id === typeId);
1472
+ if (!typeEntry) throw new Error(`Type ${typeId} not found in metadata`);
1473
+ const def = typeEntry.type.def;
1474
+ const path = typeEntry.type.path;
1475
+ if (def.primitive) {
1476
+ const codec = buildPrimitiveCodec(def.primitive);
1477
+ cache.set(typeId, codec);
1478
+ return codec;
1479
+ }
1480
+ if (path && path.length > 0) {
1481
+ const specialCodec = buildSpecialTypeCodec(path, typeEntry, types, cache);
1482
+ if (specialCodec) {
1483
+ cache.set(typeId, specialCodec);
1484
+ return specialCodec;
1485
+ }
1486
+ }
1487
+ if (def.tuple) {
1488
+ const codec = buildTupleCodec(def.tuple, types, cache);
1489
+ cache.set(typeId, codec);
1490
+ return codec;
1491
+ }
1492
+ if (def.sequence) {
1493
+ const codec = Vector(buildCodecFromTypeInternal(def.sequence.type, types, cache));
1494
+ cache.set(typeId, codec);
1495
+ return codec;
1496
+ }
1497
+ if (def.array) {
1498
+ const innerCodec = buildCodecFromTypeInternal(def.array.type, types, cache);
1499
+ if (def.array.type === findPrimitiveTypeId(types, "u8")) {
1500
+ const codec$1 = Bytes$1(def.array.len);
1501
+ cache.set(typeId, codec$1);
1502
+ return codec$1;
1503
+ }
1504
+ const codec = Vector(innerCodec, def.array.len);
1505
+ cache.set(typeId, codec);
1506
+ return codec;
1507
+ }
1508
+ if (def.composite) {
1509
+ const codec = buildCompositeCodec(def.composite, types, cache);
1510
+ cache.set(typeId, codec);
1511
+ return codec;
1512
+ }
1513
+ if (def.variant) {
1514
+ const codec = buildVariantCodec(def.variant, path, types, cache);
1515
+ cache.set(typeId, codec);
1516
+ return codec;
1517
+ }
1518
+ throw new Error(`Unknown type definition for type ${typeId}`);
1519
+ }
1520
+ function buildPrimitiveCodec(primitive) {
1521
+ switch (primitive) {
1522
+ case "u8": return u8;
1523
+ case "u16": return u16;
1524
+ case "u32": return u32;
1525
+ case "u64": return u64;
1526
+ case "u128": return u128;
1527
+ case "i8": return i8;
1528
+ case "i16": return i16;
1529
+ case "i32": return i32;
1530
+ case "i64": return i64;
1531
+ case "i128": return i128;
1532
+ case "bool": return bool;
1533
+ case "str": return str;
1534
+ default: throw new Error(`Unknown primitive type: ${primitive}`);
1535
+ }
1536
+ }
1537
+ function buildSpecialTypeCodec(path, typeEntry, types, cache) {
1538
+ if (path.join("::").includes("AccountId")) return AccountId();
1539
+ if (path[0] === "Option") {
1540
+ const params = typeEntry.type.params;
1541
+ if (params && params.length > 0 && params[0]?.type !== void 0) return Option(buildCodecFromTypeInternal(params[0].type, types, cache));
1542
+ }
1543
+ if (path[0] === "Result") {
1544
+ const params = typeEntry.type.params;
1545
+ if (params && params.length >= 2) {
1546
+ const okTypeId = params[0]?.type;
1547
+ const errTypeId = params[1]?.type;
1548
+ if (okTypeId !== void 0 && errTypeId !== void 0) return Variant({
1549
+ Ok: buildCodecFromTypeInternal(okTypeId, types, cache),
1550
+ Err: buildCodecFromTypeInternal(errTypeId, types, cache)
1551
+ }, [0, 1]);
1552
+ }
1553
+ }
1554
+ return null;
1555
+ }
1556
+ function buildTupleCodec(tupleTypes, types, cache) {
1557
+ if (tupleTypes.length === 0) return _void;
1558
+ const innerCodecs = tupleTypes.map((t) => buildCodecFromTypeInternal(t, types, cache));
1559
+ switch (innerCodecs.length) {
1560
+ case 1: return Tuple(innerCodecs[0]);
1561
+ case 2: return Tuple(innerCodecs[0], innerCodecs[1]);
1562
+ case 3: return Tuple(innerCodecs[0], innerCodecs[1], innerCodecs[2]);
1563
+ case 4: return Tuple(innerCodecs[0], innerCodecs[1], innerCodecs[2], innerCodecs[3]);
1564
+ default: return Tuple(...innerCodecs);
1565
+ }
1566
+ }
1567
+ function buildCompositeCodec(composite, types, cache) {
1568
+ const fields = composite.fields;
1569
+ if (fields.length === 1 && !fields[0]?.name) return buildCodecFromTypeInternal(fields[0].type, types, cache);
1570
+ const structDef = {};
1571
+ for (const field of fields) {
1572
+ const fieldName = field.name || `field${fields.indexOf(field)}`;
1573
+ structDef[fieldName] = buildCodecFromTypeInternal(field.type, types, cache);
1574
+ }
1575
+ return Struct(structDef);
1576
+ }
1577
+ function buildVariantCodec(variant, path, types, cache) {
1578
+ const variants = variant.variants;
1579
+ const isLangError$1 = path?.includes("LangError");
1580
+ const variantDef = {};
1581
+ const indices = [];
1582
+ if (isLangError$1 && !variants.some((v) => v.index === 0)) {
1583
+ variantDef["_Placeholder"] = _void;
1584
+ indices.push(0);
1585
+ }
1586
+ for (const v of variants) {
1587
+ let fieldCodec;
1588
+ const fields = v.fields ?? [];
1589
+ if (fields.length === 0) fieldCodec = _void;
1590
+ else if (fields.length === 1 && !fields[0]?.name) fieldCodec = buildCodecFromTypeInternal(fields[0].type, types, cache);
1591
+ else {
1592
+ const structDef = {};
1593
+ for (const field of fields) {
1594
+ const fieldName = field.name || `field${fields.indexOf(field)}`;
1595
+ structDef[fieldName] = buildCodecFromTypeInternal(field.type, types, cache);
1596
+ }
1597
+ fieldCodec = Struct(structDef);
1598
+ }
1599
+ variantDef[v.name] = fieldCodec;
1600
+ indices.push(v.index);
1601
+ }
1602
+ return Variant(variantDef, indices);
1603
+ }
1604
+ function findPrimitiveTypeId(types, primitive) {
1605
+ return types.find((t) => t.type.def.primitive === primitive)?.id ?? -1;
1606
+ }
1607
+ /**
1608
+ * Build a struct codec for message arguments
1609
+ *
1610
+ * @param metadata - The ink contract metadata
1611
+ * @param args - The argument definitions
1612
+ * @returns A struct codec
1613
+ */
1614
+ function buildArgsCodec(metadata, args) {
1615
+ if (args.length === 0) return {
1616
+ enc: () => new Uint8Array(0),
1617
+ dec: () => ({})
1618
+ };
1619
+ const types = metadata.types;
1620
+ const cache = /* @__PURE__ */ new Map();
1621
+ const structDef = {};
1622
+ for (const arg of args) structDef[arg.label] = buildCodecFromTypeInternal(arg.type.type, types, cache);
1623
+ const codec = Struct(structDef);
1624
+ return {
1625
+ enc: (value) => codec.enc(value),
1626
+ dec: (data) => codec.dec(data)
1627
+ };
1628
+ }
1629
+
1630
+ //#endregion
1631
+ //#region src/calls.ts
1632
+ /**
1633
+ * Build argument decoders for all messages in the metadata
1634
+ */
1635
+ function buildAllMessageDecodersFromMetadata(metadata) {
1636
+ const decoders = /* @__PURE__ */ new Map();
1637
+ const messages = metadata.spec.messages;
1638
+ for (const message of messages) try {
1639
+ const selectorHex = message.selector.startsWith("0x") ? message.selector.slice(2) : message.selector;
1640
+ const selector = new Uint8Array(selectorHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
1641
+ const argsCodec = buildArgsCodec(metadata, message.args);
1642
+ const decoder = (data) => argsCodec.dec(data);
1643
+ decoders.set(message.label, {
1644
+ selector,
1645
+ decoder
1235
1646
  });
1236
- } };
1647
+ } catch (error) {
1648
+ console.warn(`Failed to build decoder for message "${message.label}":`, error);
1649
+ }
1650
+ return decoders;
1651
+ }
1652
+ /**
1653
+ * Type-safe call parser for a specific contract
1654
+ *
1655
+ * @typeParam S - The storage descriptor type
1656
+ * @typeParam M - The InkCallableDescriptor type (message definitions)
1657
+ * @typeParam C - The constructors descriptor type
1658
+ * @typeParam E - The events Enum type
1659
+ */
1660
+ var ContractCallParser = class {
1661
+ messageDecoders;
1662
+ selectorToLabel;
1663
+ metadata;
1664
+ constructor(descriptor) {
1665
+ if (!descriptor.metadata) throw new Error("Contract descriptor must include metadata");
1666
+ this.metadata = descriptor.metadata;
1667
+ this.messageDecoders = buildAllMessageDecodersFromMetadata(this.metadata);
1668
+ this.selectorToLabel = /* @__PURE__ */ new Map();
1669
+ for (const [label, { selector }] of this.messageDecoders) {
1670
+ const selectorHex = Array.from(selector).map((b) => b.toString(16).padStart(2, "0")).join("");
1671
+ this.selectorToLabel.set(selectorHex, label);
1672
+ }
1673
+ }
1674
+ /**
1675
+ * Parse raw call data into a type-safe contract call
1676
+ *
1677
+ * @param callData - The raw call data (selector + encoded args) or RawContractCall
1678
+ * @returns Parsed call or null if cannot parse
1679
+ *
1680
+ * @example
1681
+ * ```ts
1682
+ * const call = parser.parseCall(callData);
1683
+ * if (call?.type === "PSP22::transfer") {
1684
+ * // call.args is typed as { to: SS58String; value: bigint; data: Uint8Array }
1685
+ * console.log(call.args.to);
1686
+ * }
1687
+ * ```
1688
+ */
1689
+ parseCall(callData) {
1690
+ const data = callData instanceof Uint8Array ? callData : callData.data;
1691
+ const raw = callData instanceof Uint8Array ? { data: callData } : callData;
1692
+ if (data.length < 4) return null;
1693
+ const selector = data.slice(0, 4);
1694
+ const selectorHex = Array.from(selector).map((b) => b.toString(16).padStart(2, "0")).join("");
1695
+ const label = this.selectorToLabel.get(selectorHex);
1696
+ if (!label) return null;
1697
+ const messageInfo = this.messageDecoders.get(label);
1698
+ if (!messageInfo) return null;
1699
+ const argsData = data.slice(4);
1700
+ try {
1701
+ return {
1702
+ type: label,
1703
+ args: messageInfo.decoder(argsData),
1704
+ selector,
1705
+ raw
1706
+ };
1707
+ } catch (error) {
1708
+ console.warn(`Failed to decode call "${label}":`, error);
1709
+ return null;
1710
+ }
1711
+ }
1712
+ /**
1713
+ * Parse multiple calls and optionally filter by message labels
1714
+ *
1715
+ * @param calls - Array of raw call data
1716
+ * @param options - Filter options
1717
+ * @returns Array of parsed calls
1718
+ */
1719
+ parseCalls(calls, options) {
1720
+ const results = [];
1721
+ for (const call of calls) {
1722
+ const parsed = this.parseCall(call);
1723
+ if (!parsed) continue;
1724
+ if (options?.messageLabels && !options.messageLabels.includes(parsed.type)) continue;
1725
+ results.push(parsed);
1726
+ }
1727
+ return results;
1728
+ }
1729
+ /**
1730
+ * Filter calls by specific type with proper type narrowing
1731
+ *
1732
+ * This method provides better type safety than parseCalls with messageLabels
1733
+ * because TypeScript can narrow the return type to only the specified call type.
1734
+ *
1735
+ * @param calls - Array of raw call data
1736
+ * @param label - The message label to filter by
1737
+ * @returns Array of calls narrowed to the specific type
1738
+ *
1739
+ * @example
1740
+ * ```ts
1741
+ * const transfers = parser.filterByType(callDataArray, "PSP22::transfer");
1742
+ *
1743
+ * for (const t of transfers) {
1744
+ * // t.args is fully typed as PSP22::transfer args
1745
+ * console.log(t.args.to, t.args.value);
1746
+ * }
1747
+ * ```
1748
+ */
1749
+ filterByType(calls, label) {
1750
+ return this.parseCalls(calls).filter((c) => c.type === label);
1751
+ }
1752
+ /**
1753
+ * Get information about a message by label
1754
+ */
1755
+ getMessageInfo(label) {
1756
+ const info = this.messageDecoders.get(label);
1757
+ if (!info) return null;
1758
+ const message = this.metadata.spec.messages.find((m) => m.label === label);
1759
+ if (!message) return null;
1760
+ return {
1761
+ label: message.label,
1762
+ selector: info.selector,
1763
+ mutates: message.mutates,
1764
+ payable: message.payable,
1765
+ args: message.args
1766
+ };
1767
+ }
1768
+ /**
1769
+ * Get all available message labels
1770
+ */
1771
+ getMessageLabels() {
1772
+ return Array.from(this.messageDecoders.keys());
1773
+ }
1774
+ /**
1775
+ * Check if a selector matches a specific message
1776
+ */
1777
+ matchesMessage(selector, label) {
1778
+ const info = this.messageDecoders.get(label);
1779
+ if (!info) return false;
1780
+ if (selector.length !== info.selector.length) return false;
1781
+ for (let i = 0; i < selector.length; i++) if (selector[i] !== info.selector[i]) return false;
1782
+ return true;
1783
+ }
1784
+ };
1785
+ /**
1786
+ * Type guard for narrowing call types
1787
+ *
1788
+ * Use this function when you have a `TypedContractCall` and need to
1789
+ * narrow it to a specific call type. This is useful when the type
1790
+ * information is lost (e.g., after serialization or in generic functions).
1791
+ *
1792
+ * @typeParam M - The InkCallableDescriptor type (message definitions)
1793
+ * @typeParam L - The specific message label to check
1794
+ * @param call - The call to check
1795
+ * @param label - The message label to match
1796
+ * @returns True if the call type matches the label
1797
+ *
1798
+ * @example
1799
+ * ```ts
1800
+ * const call = parser.parseCall(callData);
1801
+ *
1802
+ * if (call && isCallType(call, "PSP22::transfer")) {
1803
+ * // TypeScript knows call.args is transfer args
1804
+ * console.log(call.args.to);
1805
+ * console.log(call.args.value);
1806
+ * }
1807
+ * ```
1808
+ */
1809
+ function isCallType(call, label) {
1810
+ return call.type === label;
1237
1811
  }
1238
1812
 
1239
1813
  //#endregion
1240
- export { AbortedError, ContractError, ContractEventParser, ContractExecutionError, DecodeError, EncodeError, InkCodecs, LangError, MetadataError, NetworkError, SignerError, TimeoutError, TransactionError, buildAllEventDecoders, buildAllMessageDecoders, buildEventDecoder, buildMessageDecoder, createCodecRegistry, createContractEventStream, createD9InkContract, createD9InkSdk, createNativeTransferStream, createPSP22TransferStream, decodeContractCallResult, decodeInkValue, decodeResult, encodeCall, encodeContractCall, encodeContractCallWithLimits, getEventSignature, isContractError, isErrorType, isLangError, unwrapInkResult };
1814
+ export { AbortedError, ContractCallParser, ContractError, ContractEventParser, ContractExecutionError, DecodeError, EncodeError, InkCodecs, LangError, MetadataError, NetworkError, SignerError, TimeoutError, TransactionError, buildAllEventDecoders, buildAllMessageDecoders, buildEventDecoder, buildMessageDecoder, createCodecRegistry, createContractEventStream, createD9InkContract, createD9InkSdk, createMessageBuilder, createNativeTransferStream, createPSP22TransferStream, createTypedRpc, decodeContractCallResult, decodeInkValue, decodeResult, encodeCall, encodeContractCall, encodeContractCallWithLimits, getEventSignature, isCallType, isContractError, isErrorType, isEventType, isLangError, unwrapInkResult };
1241
1815
  //# sourceMappingURL=index.mjs.map