@d9-network/ink 0.0.5 → 0.0.7

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.cjs 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 = (0, _polkadot_api_substrate_bindings.Bytes)(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 _polkadot_api_substrate_bindings.u8;
260
260
  case "u16": return _polkadot_api_substrate_bindings.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 (0, _polkadot_api_substrate_bindings.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 _polkadot_api_substrate_bindings._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
  /**
@@ -466,11 +466,37 @@ function buildAllEventDecoders(metadata) {
466
466
  function getEventSignature(eventLabel) {
467
467
  return (0, _noble_hashes_blake2_js.blake2b)(new TextEncoder().encode(eventLabel), { dkLen: 32 });
468
468
  }
469
+ /**
470
+ * Create ASCII-encoded event topic (D9 chain format)
471
+ *
472
+ * D9 chain uses a format where:
473
+ * - First byte is 0x00 (null)
474
+ * - Remaining 31 bytes are ASCII characters of `ContractName::EventLabel`
475
+ * - Total is exactly 32 bytes (truncated/padded as needed)
476
+ *
477
+ * @param contractName - Contract name (e.g., "MarketMaker", "Usdt")
478
+ * @param eventLabel - Event label (e.g., "Transfer", "USDTToD9Conversion")
479
+ * @returns 32-byte topic with null prefix and ASCII-encoded path
480
+ */
481
+ function createAsciiEventTopic(contractName, eventLabel) {
482
+ const topic = new Uint8Array(32);
483
+ topic[0] = 0;
484
+ const encoder = new TextEncoder();
485
+ const fullPath = `${contractName}::${eventLabel}`;
486
+ const encoded = encoder.encode(fullPath);
487
+ topic.set(encoded.slice(0, 31), 1);
488
+ return topic;
489
+ }
469
490
 
470
491
  //#endregion
471
492
  //#region src/events.ts
472
493
  /**
473
- * Event parser for a specific contract
494
+ * Type-safe event parser for a specific contract
495
+ *
496
+ * @typeParam S - The storage descriptor type
497
+ * @typeParam M - The messages descriptor type
498
+ * @typeParam C - The constructors descriptor type
499
+ * @typeParam E - The event type representing all possible events for this contract
474
500
  */
475
501
  var ContractEventParser = class {
476
502
  eventDecoders;
@@ -478,21 +504,31 @@ var ContractEventParser = class {
478
504
  contractAddressBytes;
479
505
  contractAddress;
480
506
  metadata;
481
- constructor(metadata, contractAddress) {
482
- this.metadata = metadata;
507
+ constructor(descriptor, contractAddress) {
508
+ if (!descriptor.metadata) throw new Error("Contract descriptor must include metadata");
509
+ this.metadata = descriptor.metadata;
483
510
  this.contractAddress = contractAddress;
484
- this.eventDecoders = buildAllEventDecoders(metadata);
511
+ this.eventDecoders = buildAllEventDecoders(this.metadata);
485
512
  this.contractAddressBytes = (0, _polkadot_labs_hdkd_helpers.ss58Decode)(contractAddress)[0];
486
513
  this.eventSignatures = /* @__PURE__ */ new Map();
487
- const events = metadata.spec.events;
514
+ const events = this.metadata.spec.events;
488
515
  for (const event of events) {
489
516
  const sig = getEventSignature(event.label);
490
517
  this.eventSignatures.set(event.label, sig);
491
518
  }
492
519
  }
493
520
  /**
494
- * Parse a raw chain event into a contract event (if it matches)
521
+ * Parse a raw chain event into a type-safe contract event (if it matches)
495
522
  * Returns null if the event is not from this contract or cannot be parsed
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * const event = parser.parseEvent(chainEvent);
527
+ * if (event?.type === "Transfer") {
528
+ * // event.value is now typed as { from: SS58String; to: SS58String; value: bigint }
529
+ * console.log(event.value.from);
530
+ * }
531
+ * ```
496
532
  */
497
533
  parseEvent(chainEvent) {
498
534
  const extracted = this.extractContractEmittedEvent(chainEvent);
@@ -502,9 +538,13 @@ var ContractEventParser = class {
502
538
  if (topics.length === 0) return null;
503
539
  const signature = topics[0];
504
540
  let eventLabel = null;
505
- for (const [label, sig] of this.eventSignatures) if (this.bytesEqual(signature, sig)) {
506
- eventLabel = label;
507
- break;
541
+ eventLabel = this.extractEventLabelFromAsciiTopic(signature);
542
+ if (eventLabel && !this.eventDecoders.has(eventLabel)) eventLabel = null;
543
+ if (!eventLabel) {
544
+ for (const [label, sig] of this.eventSignatures) if (this.bytesEqual(signature, sig)) {
545
+ eventLabel = label;
546
+ break;
547
+ }
508
548
  }
509
549
  if (!eventLabel) {
510
550
  console.warn("Unknown event signature:", signature);
@@ -518,8 +558,8 @@ var ContractEventParser = class {
518
558
  try {
519
559
  const decodedData = decoder(data);
520
560
  return {
521
- label: eventLabel,
522
- data: decodedData,
561
+ type: eventLabel,
562
+ value: decodedData,
523
563
  raw: {
524
564
  blockNumber,
525
565
  blockHash,
@@ -535,14 +575,37 @@ var ContractEventParser = class {
535
575
  }
536
576
  }
537
577
  /**
538
- * Filter a batch of events
578
+ * Extract event label from ASCII-encoded topic (D9 chain format)
579
+ *
580
+ * D9 chain uses a format where:
581
+ * - First byte is 0x00 (null)
582
+ * - Remaining 31 bytes are ASCII characters of `ContractName::EventLabel`
583
+ * - Total is exactly 32 bytes (truncated/padded as needed)
584
+ *
585
+ * @param topic - The 32-byte topic from the event
586
+ * @returns The extracted event label, or null if not in ASCII format
587
+ */
588
+ extractEventLabelFromAsciiTopic(topic) {
589
+ if (topic.length !== 32) return null;
590
+ if (topic[0] !== 0) return null;
591
+ const ascii = new TextDecoder().decode(topic.slice(1)).replace(/\0+$/, "");
592
+ const colonIndex = ascii.lastIndexOf("::");
593
+ if (colonIndex !== -1) return ascii.slice(colonIndex + 2);
594
+ return ascii || null;
595
+ }
596
+ /**
597
+ * Filter a batch of events and return type-safe results
598
+ *
599
+ * @param chainEvents - Array of chain events to filter
600
+ * @param options - Optional filter criteria
601
+ * @returns Array of type-safe contract events
539
602
  */
540
603
  filterEvents(chainEvents, options) {
541
604
  const results = [];
542
605
  for (const chainEvent of chainEvents) {
543
606
  const parsed = this.parseEvent(chainEvent);
544
607
  if (!parsed) continue;
545
- if (options?.eventLabels && !options.eventLabels.includes(parsed.label)) continue;
608
+ if (options?.eventLabels && !options.eventLabels.includes(parsed.type)) continue;
546
609
  if (options?.fromBlock && parsed.raw.blockNumber < options.fromBlock) continue;
547
610
  if (options?.toBlock && parsed.raw.blockNumber > options.toBlock) continue;
548
611
  results.push(parsed);
@@ -550,6 +613,29 @@ var ContractEventParser = class {
550
613
  return results;
551
614
  }
552
615
  /**
616
+ * Filter events by specific type with proper type narrowing
617
+ *
618
+ * This method provides better type safety than filterEvents with eventLabels
619
+ * because TypeScript can narrow the return type to only the specified event type.
620
+ *
621
+ * @param chainEvents - Array of chain events to filter
622
+ * @param label - The event label to filter by
623
+ * @returns Array of events narrowed to the specific type
624
+ *
625
+ * @example
626
+ * ```ts
627
+ * const transfers = parser.filterByType(events, "Transfer");
628
+ *
629
+ * for (const t of transfers) {
630
+ * // t.value is fully typed as Transfer event data
631
+ * console.log(t.value.from, "->", t.value.to, ":", t.value.value);
632
+ * }
633
+ * ```
634
+ */
635
+ filterByType(chainEvents, label) {
636
+ return this.filterEvents(chainEvents).filter((e) => e.type === label);
637
+ }
638
+ /**
553
639
  * Get the contract address as SS58 string
554
640
  */
555
641
  getContractAddress() {
@@ -570,20 +656,31 @@ var ContractEventParser = class {
570
656
  if (eventValue.type !== "ContractEmitted") return null;
571
657
  const contractEmittedData = eventValue.value;
572
658
  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;
659
+ const contractRaw = contractEmittedData.contract;
660
+ const dataRaw = contractEmittedData.data;
661
+ let contract;
662
+ if (contractRaw instanceof Uint8Array) contract = contractRaw;
663
+ else if (typeof contractRaw === "string") contract = (0, _polkadot_labs_hdkd_helpers.ss58Decode)(contractRaw)[0];
664
+ else return null;
665
+ let data;
666
+ if (dataRaw instanceof Uint8Array) data = dataRaw;
667
+ else if (dataRaw && typeof dataRaw === "object" && "asBytes" in dataRaw && typeof dataRaw.asBytes === "function") data = dataRaw.asBytes();
668
+ else return null;
576
669
  const topics = [];
577
670
  if (record.topics && Array.isArray(record.topics)) {
578
671
  for (const topic of record.topics) if (topic instanceof Uint8Array) topics.push(topic);
672
+ else if (topic && typeof topic === "object" && "asBytes" in topic && typeof topic.asBytes === "function") topics.push(topic.asBytes());
579
673
  }
674
+ const blockNumber = typeof record.blockNumber === "number" ? record.blockNumber : 0;
675
+ const blockHash = typeof record.blockHash === "string" ? record.blockHash : "";
676
+ const eventIndex = typeof record.eventIndex === "number" ? record.eventIndex : 0;
580
677
  return {
581
678
  contract,
582
679
  data,
583
680
  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
681
+ blockNumber,
682
+ blockHash,
683
+ eventIndex
587
684
  };
588
685
  }
589
686
  /**
@@ -594,19 +691,35 @@ var ContractEventParser = class {
594
691
  for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
595
692
  return true;
596
693
  }
597
- /**
598
- * Build event signature map from metadata
599
- */
600
- static buildEventSignatureMap(metadata) {
601
- const signatures = /* @__PURE__ */ new Map();
602
- const events = metadata.spec.events;
603
- for (const event of events) {
604
- const sig = getEventSignature(event.label);
605
- signatures.set(event.label, sig);
606
- }
607
- return signatures;
608
- }
609
694
  };
695
+ /**
696
+ * Type guard for narrowing event types
697
+ *
698
+ * Use this function when you have a `TypedContractEvent` and need to
699
+ * narrow it to a specific event type. This is useful when the type
700
+ * information is lost (e.g., after serialization or in generic functions).
701
+ *
702
+ * @typeParam E - The Enum type representing all possible events
703
+ * @typeParam L - The specific event label to check
704
+ * @param event - The event to check
705
+ * @param label - The event label to match
706
+ * @returns True if the event type matches the label
707
+ *
708
+ * @example
709
+ * ```ts
710
+ * const event = parser.parseEvent(chainEvent);
711
+ *
712
+ * if (event && isEventType(event, "Transfer")) {
713
+ * // TypeScript knows event.value is Transfer event data
714
+ * console.log(event.value.from);
715
+ * console.log(event.value.to);
716
+ * console.log(event.value.value);
717
+ * }
718
+ * ```
719
+ */
720
+ function isEventType(event, label) {
721
+ return event.type === label;
722
+ }
610
723
 
611
724
  //#endregion
612
725
  //#region src/subscriptions.ts
@@ -617,12 +730,12 @@ var ContractEventParser = class {
617
730
  * Create an observable stream of contract events
618
731
  *
619
732
  * @param client - Polkadot API client
620
- * @param metadata - Contract metadata
733
+ * @param descriptor - Contract descriptor
621
734
  * @param options - Subscription options
622
- * @returns Observable stream of decoded contract events
735
+ * @returns Observable stream of type-safe contract events
623
736
  */
624
- function createContractEventStream(client, metadata, options) {
625
- const parser = new ContractEventParser(metadata, options.contractAddress);
737
+ function createContractEventStream(client, descriptor, options) {
738
+ const parser = new ContractEventParser(descriptor, options.contractAddress);
626
739
  return client.finalizedBlock$.pipe((0, rxjs.mergeMap)(async (block) => {
627
740
  try {
628
741
  return {
@@ -648,7 +761,7 @@ function createContractEventStream(client, metadata, options) {
648
761
  }).filter((e) => e !== null);
649
762
  }), (0, rxjs.mergeMap)((events) => (0, rxjs.from)(events)), (0, rxjs.filter)((event) => {
650
763
  if (!options.eventLabels) return true;
651
- return options.eventLabels.includes(event.label);
764
+ return options.eventLabels.includes(event.type);
652
765
  }), (0, rxjs.catchError)((error) => {
653
766
  console.error("Error in contract event stream:", error);
654
767
  return (0, rxjs.of)();
@@ -658,21 +771,21 @@ function createContractEventStream(client, metadata, options) {
658
771
  * Convenience helper to create a Transfer event stream for PSP22 tokens
659
772
  *
660
773
  * @param client - Polkadot API client
661
- * @param metadata - PSP22 contract metadata
774
+ * @param descriptor - PSP22 contract descriptor
662
775
  * @param contractAddress - PSP22 contract address
663
776
  * @param getEvents - Function to fetch System.Events at a block hash
664
777
  * @param watchAddress - Optional address to filter transfers (only events involving this address)
665
778
  * @returns Observable stream of Transfer events
666
779
  */
667
- function createPSP22TransferStream(client, metadata, contractAddress, getEvents, watchAddress) {
668
- return createContractEventStream(client, metadata, {
780
+ function createPSP22TransferStream(client, descriptor, contractAddress, getEvents, watchAddress) {
781
+ return createContractEventStream(client, descriptor, {
669
782
  contractAddress,
670
783
  eventLabels: ["Transfer"],
671
784
  getEvents
672
785
  }).pipe((0, rxjs.filter)((event) => {
673
786
  if (!watchAddress) return true;
674
- const data = event.data;
675
- return data.from === watchAddress || data.to === watchAddress;
787
+ const value = event.value;
788
+ return value.from === watchAddress || value.to === watchAddress;
676
789
  }));
677
790
  }
678
791
  /**
@@ -719,6 +832,91 @@ function createNativeTransferStream(client, getEvents, watchAddress) {
719
832
  }), (0, rxjs.share)());
720
833
  }
721
834
 
835
+ //#endregion
836
+ //#region src/message-builder.ts
837
+ /**
838
+ * Create a type-safe message builder from a contract descriptor
839
+ *
840
+ * @typeParam S - Storage descriptor type
841
+ * @typeParam M - Messages descriptor type
842
+ * @typeParam C - Constructors descriptor type
843
+ * @typeParam E - Events Enum type
844
+ * @param descriptor - The ink! contract descriptor containing metadata
845
+ * @returns A ContractMessageBuilder instance
846
+ *
847
+ * @example
848
+ * ```ts
849
+ * import { createMessageBuilder } from '@d9-network/ink';
850
+ * import { contracts } from '@polkadot-api/descriptors';
851
+ *
852
+ * const builder = createMessageBuilder(contracts.usdt);
853
+ *
854
+ * // Get a typed message interface
855
+ * const transfer = builder.message("PSP22::transfer");
856
+ *
857
+ * // Encode with full type checking on args
858
+ * const encoded = transfer.encode({
859
+ * to: recipientAddress, // Must be SS58String
860
+ * value: 1000000n, // Must be bigint
861
+ * data: new Uint8Array(), // Must be Uint8Array
862
+ * });
863
+ *
864
+ * // Decode response with full type inference
865
+ * const response = transfer.decode(resultBytes);
866
+ * ```
867
+ */
868
+ function createMessageBuilder(descriptor) {
869
+ if (!descriptor.metadata) throw new Error("Contract descriptor must include metadata");
870
+ const metadata = descriptor.metadata;
871
+ const builder = (0, _polkadot_api_ink_contracts.getInkDynamicBuilder)((0, _polkadot_api_ink_contracts.getInkLookup)(metadata));
872
+ const codecCache = /* @__PURE__ */ new Map();
873
+ const messagesMetadata = /* @__PURE__ */ new Map();
874
+ for (const msg of metadata.spec.messages) messagesMetadata.set(msg.label, msg);
875
+ function getMessageCodec(label) {
876
+ const cached = codecCache.get(label);
877
+ if (cached) return cached;
878
+ const codec = builder.buildMessage(label);
879
+ codecCache.set(label, codec);
880
+ return codec;
881
+ }
882
+ function parseSelector(selectorHex) {
883
+ const hex = selectorHex.startsWith("0x") ? selectorHex.slice(2) : selectorHex;
884
+ return new Uint8Array(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
885
+ }
886
+ function message(label) {
887
+ const msgMeta = messagesMetadata.get(label);
888
+ if (!msgMeta) throw new Error(`Message "${label}" not found in metadata`);
889
+ const codec = getMessageCodec(label);
890
+ const selector = parseSelector(msgMeta.selector);
891
+ const encode = (args) => {
892
+ const encoded = codec.call.enc(args ?? {});
893
+ return polkadot_api.Binary.fromBytes(encoded);
894
+ };
895
+ const decode = (response) => {
896
+ const bytes = response instanceof Uint8Array ? response : response.asBytes();
897
+ return codec.value.dec(bytes);
898
+ };
899
+ return {
900
+ encode,
901
+ decode,
902
+ attributes: {
903
+ mutates: msgMeta.mutates,
904
+ payable: msgMeta.payable,
905
+ default: msgMeta.default
906
+ },
907
+ selector,
908
+ label
909
+ };
910
+ }
911
+ function getMessageLabels() {
912
+ return Array.from(messagesMetadata.keys());
913
+ }
914
+ return {
915
+ message,
916
+ getMessageLabels
917
+ };
918
+ }
919
+
722
920
  //#endregion
723
921
  //#region src/errors.ts
724
922
  /**
@@ -937,6 +1135,10 @@ function createD9InkContract(descriptor, address, options) {
937
1135
  if (!descriptor.metadata) throw new MetadataError("Contract descriptor must include metadata");
938
1136
  const patchedMetadata = patchLangErrorInMetadata(descriptor.metadata);
939
1137
  const builder = (0, _polkadot_api_ink_contracts.getInkDynamicBuilder)((0, _polkadot_api_ink_contracts.getInkLookup)(patchedMetadata));
1138
+ const patchedDescriptor = {
1139
+ ...descriptor,
1140
+ metadata: patchedMetadata
1141
+ };
940
1142
  let codecRegistry;
941
1143
  try {
942
1144
  codecRegistry = createCodecRegistry(patchedMetadata);
@@ -971,13 +1173,13 @@ function createD9InkContract(descriptor, address, options) {
971
1173
  checkAborted(signal, method);
972
1174
  const originBytes = ss58ToBytes(origin);
973
1175
  const codec = getMessageCodec(method);
974
- const message = encodeContractCall(originBytes, addressBytes, codec.call.enc(args ?? {}), value);
1176
+ const message$1 = encodeContractCall(originBytes, addressBytes, codec.call.enc(args ?? {}), value);
975
1177
  const blockHash = at ?? (await client.getFinalizedBlock()).hash;
976
1178
  checkAborted(signal, method);
977
1179
  const executeCall = async () => {
978
1180
  return (0, polkadot_api_utils.fromHex)(await client._request("state_call", [
979
1181
  "ContractsApi_call",
980
- message,
1182
+ message$1,
981
1183
  blockHash
982
1184
  ]));
983
1185
  };
@@ -1079,11 +1281,11 @@ function createD9InkContract(descriptor, address, options) {
1079
1281
  try {
1080
1282
  let gas = gasLimit;
1081
1283
  if (!gas) {
1082
- const message = encodeContractCall(originBytes, addressBytes, callData, value);
1284
+ const message$1 = encodeContractCall(originBytes, addressBytes, callData, value);
1083
1285
  const blockHash = (await client.getFinalizedBlock()).hash;
1084
1286
  gas = decodeContractCallResult((0, polkadot_api_utils.fromHex)(await client._request("state_call", [
1085
1287
  "ContractsApi_call",
1086
- message,
1288
+ message$1,
1087
1289
  blockHash
1088
1290
  ]))).gas.gasRequired;
1089
1291
  }
@@ -1152,7 +1354,20 @@ function createD9InkContract(descriptor, address, options) {
1152
1354
  * Filter events for this contract
1153
1355
  */
1154
1356
  function filterEvents(events) {
1155
- return new ContractEventParser(patchedMetadata, address).filterEvents(events);
1357
+ return new ContractEventParser(patchedDescriptor, address).filterEvents(events);
1358
+ }
1359
+ /**
1360
+ * Filter events by specific type with proper type narrowing
1361
+ */
1362
+ function filterEventsByType(events, label) {
1363
+ return new ContractEventParser(patchedDescriptor, address).filterByType(events, label);
1364
+ }
1365
+ const messageBuilder = createMessageBuilder(descriptor);
1366
+ /**
1367
+ * Get a type-safe message interface
1368
+ */
1369
+ function message(label) {
1370
+ return messageBuilder.message(label);
1156
1371
  }
1157
1372
  /**
1158
1373
  * Subscribe to contract events as an RxJS Observable
@@ -1163,7 +1378,7 @@ function createD9InkContract(descriptor, address, options) {
1163
1378
  * @param options.fromBlock - Optional starting block number
1164
1379
  */
1165
1380
  function subscribeToEvents(options$1) {
1166
- return createContractEventStream(client, patchedMetadata, {
1381
+ return createContractEventStream(client, patchedDescriptor, {
1167
1382
  ...options$1,
1168
1383
  contractAddress: address
1169
1384
  });
@@ -1175,10 +1390,43 @@ function createD9InkContract(descriptor, address, options) {
1175
1390
  send,
1176
1391
  getStorage,
1177
1392
  filterEvents,
1393
+ filterEventsByType,
1394
+ message,
1178
1395
  subscribeToEvents
1179
1396
  };
1180
1397
  }
1181
1398
 
1399
+ //#endregion
1400
+ //#region src/rpc.ts
1401
+ /**
1402
+ * Create a type-safe RPC request function from a PolkadotClient
1403
+ *
1404
+ * This wraps the client's `_request` method with proper TypeScript types,
1405
+ * providing autocomplete for known RPC methods and type inference for
1406
+ * parameters and return values.
1407
+ *
1408
+ * @param client - The PolkadotClient instance
1409
+ * @returns A type-safe RPC request function
1410
+ *
1411
+ * @example
1412
+ * ```ts
1413
+ * const rpc = createTypedRpc(client);
1414
+ *
1415
+ * // Autocomplete for method names, typed params and return
1416
+ * const hash = await rpc("chain_getBlockHash", [12345]);
1417
+ * // hash: HexString | null
1418
+ *
1419
+ * const header = await rpc("chain_getHeader", [hash]);
1420
+ * // header: BlockHeader | null
1421
+ *
1422
+ * // Custom methods still work with explicit types
1423
+ * const custom = await rpc<MyType>("my_custom_method", [arg]);
1424
+ * ```
1425
+ */
1426
+ function createTypedRpc(client) {
1427
+ return ((method, params) => client._request(method, params));
1428
+ }
1429
+
1182
1430
  //#endregion
1183
1431
  //#region src/sdk.ts
1184
1432
  /**
@@ -1222,22 +1470,502 @@ function createD9InkContract(descriptor, address, options) {
1222
1470
  *
1223
1471
  * @param client - The PolkadotClient instance
1224
1472
  * @param options - Optional SDK configuration
1225
- * @returns D9 Ink SDK instance
1473
+ * @returns D9 Ink SDK instance with typed RPC access
1226
1474
  */
1227
1475
  function createD9InkSdk(client, options = {}) {
1228
1476
  const { typedApi, defaultQueryOptions, defaultSendOptions } = options;
1229
- return { getContract(descriptor, address) {
1230
- return createD9InkContract(descriptor, address, {
1231
- client,
1232
- typedApi,
1233
- defaultQueryOptions,
1234
- defaultSendOptions
1477
+ return {
1478
+ getContract(descriptor, address) {
1479
+ return createD9InkContract(descriptor, address, {
1480
+ client,
1481
+ typedApi,
1482
+ defaultQueryOptions,
1483
+ defaultSendOptions
1484
+ });
1485
+ },
1486
+ rpc: createTypedRpc(client)
1487
+ };
1488
+ }
1489
+
1490
+ //#endregion
1491
+ //#region src/codec-builder-internal.ts
1492
+ /**
1493
+ * Internal codec building utilities
1494
+ *
1495
+ * This module provides lower-level codec building from metadata type IDs.
1496
+ */
1497
+ /**
1498
+ * Build a SCALE codec from ink metadata type definition
1499
+ */
1500
+ function buildCodecFromTypeInternal(typeId, types, cache) {
1501
+ const cached = cache.get(typeId);
1502
+ if (cached) return cached;
1503
+ const typeEntry = types.find((t) => t.id === typeId);
1504
+ if (!typeEntry) throw new Error(`Type ${typeId} not found in metadata`);
1505
+ const def = typeEntry.type.def;
1506
+ const path = typeEntry.type.path;
1507
+ if (def.primitive) {
1508
+ const codec = buildPrimitiveCodec(def.primitive);
1509
+ cache.set(typeId, codec);
1510
+ return codec;
1511
+ }
1512
+ if (path && path.length > 0) {
1513
+ const specialCodec = buildSpecialTypeCodec(path, typeEntry, types, cache);
1514
+ if (specialCodec) {
1515
+ cache.set(typeId, specialCodec);
1516
+ return specialCodec;
1517
+ }
1518
+ }
1519
+ if (def.tuple) {
1520
+ const codec = buildTupleCodec(def.tuple, types, cache);
1521
+ cache.set(typeId, codec);
1522
+ return codec;
1523
+ }
1524
+ if (def.sequence) {
1525
+ const codec = (0, _polkadot_api_substrate_bindings.Vector)(buildCodecFromTypeInternal(def.sequence.type, types, cache));
1526
+ cache.set(typeId, codec);
1527
+ return codec;
1528
+ }
1529
+ if (def.array) {
1530
+ const innerCodec = buildCodecFromTypeInternal(def.array.type, types, cache);
1531
+ if (def.array.type === findPrimitiveTypeId(types, "u8")) {
1532
+ const codec$1 = (0, _polkadot_api_substrate_bindings.Bytes)(def.array.len);
1533
+ cache.set(typeId, codec$1);
1534
+ return codec$1;
1535
+ }
1536
+ const codec = (0, _polkadot_api_substrate_bindings.Vector)(innerCodec, def.array.len);
1537
+ cache.set(typeId, codec);
1538
+ return codec;
1539
+ }
1540
+ if (def.composite) {
1541
+ const codec = buildCompositeCodec(def.composite, types, cache);
1542
+ cache.set(typeId, codec);
1543
+ return codec;
1544
+ }
1545
+ if (def.variant) {
1546
+ const codec = buildVariantCodec(def.variant, path, types, cache);
1547
+ cache.set(typeId, codec);
1548
+ return codec;
1549
+ }
1550
+ throw new Error(`Unknown type definition for type ${typeId}`);
1551
+ }
1552
+ function buildPrimitiveCodec(primitive) {
1553
+ switch (primitive) {
1554
+ case "u8": return _polkadot_api_substrate_bindings.u8;
1555
+ case "u16": return _polkadot_api_substrate_bindings.u16;
1556
+ case "u32": return _polkadot_api_substrate_bindings.u32;
1557
+ case "u64": return _polkadot_api_substrate_bindings.u64;
1558
+ case "u128": return _polkadot_api_substrate_bindings.u128;
1559
+ case "i8": return _polkadot_api_substrate_bindings.i8;
1560
+ case "i16": return _polkadot_api_substrate_bindings.i16;
1561
+ case "i32": return _polkadot_api_substrate_bindings.i32;
1562
+ case "i64": return _polkadot_api_substrate_bindings.i64;
1563
+ case "i128": return _polkadot_api_substrate_bindings.i128;
1564
+ case "bool": return _polkadot_api_substrate_bindings.bool;
1565
+ case "str": return _polkadot_api_substrate_bindings.str;
1566
+ default: throw new Error(`Unknown primitive type: ${primitive}`);
1567
+ }
1568
+ }
1569
+ function buildSpecialTypeCodec(path, typeEntry, types, cache) {
1570
+ if (path.join("::").includes("AccountId")) return (0, _polkadot_api_substrate_bindings.AccountId)();
1571
+ if (path[0] === "Option") {
1572
+ const params = typeEntry.type.params;
1573
+ if (params && params.length > 0 && params[0]?.type !== void 0) return (0, _polkadot_api_substrate_bindings.Option)(buildCodecFromTypeInternal(params[0].type, types, cache));
1574
+ }
1575
+ if (path[0] === "Result") {
1576
+ const params = typeEntry.type.params;
1577
+ if (params && params.length >= 2) {
1578
+ const okTypeId = params[0]?.type;
1579
+ const errTypeId = params[1]?.type;
1580
+ if (okTypeId !== void 0 && errTypeId !== void 0) return (0, _polkadot_api_substrate_bindings.Variant)({
1581
+ Ok: buildCodecFromTypeInternal(okTypeId, types, cache),
1582
+ Err: buildCodecFromTypeInternal(errTypeId, types, cache)
1583
+ }, [0, 1]);
1584
+ }
1585
+ }
1586
+ return null;
1587
+ }
1588
+ function buildTupleCodec(tupleTypes, types, cache) {
1589
+ if (tupleTypes.length === 0) return _polkadot_api_substrate_bindings._void;
1590
+ const innerCodecs = tupleTypes.map((t) => buildCodecFromTypeInternal(t, types, cache));
1591
+ switch (innerCodecs.length) {
1592
+ case 1: return (0, _polkadot_api_substrate_bindings.Tuple)(innerCodecs[0]);
1593
+ case 2: return (0, _polkadot_api_substrate_bindings.Tuple)(innerCodecs[0], innerCodecs[1]);
1594
+ case 3: return (0, _polkadot_api_substrate_bindings.Tuple)(innerCodecs[0], innerCodecs[1], innerCodecs[2]);
1595
+ case 4: return (0, _polkadot_api_substrate_bindings.Tuple)(innerCodecs[0], innerCodecs[1], innerCodecs[2], innerCodecs[3]);
1596
+ default: return (0, _polkadot_api_substrate_bindings.Tuple)(...innerCodecs);
1597
+ }
1598
+ }
1599
+ function buildCompositeCodec(composite, types, cache) {
1600
+ const fields = composite.fields;
1601
+ if (fields.length === 1 && !fields[0]?.name) return buildCodecFromTypeInternal(fields[0].type, types, cache);
1602
+ const structDef = {};
1603
+ for (const field of fields) {
1604
+ const fieldName = field.name || `field${fields.indexOf(field)}`;
1605
+ structDef[fieldName] = buildCodecFromTypeInternal(field.type, types, cache);
1606
+ }
1607
+ return (0, _polkadot_api_substrate_bindings.Struct)(structDef);
1608
+ }
1609
+ function buildVariantCodec(variant, path, types, cache) {
1610
+ const variants = variant.variants;
1611
+ const isLangError$1 = path?.includes("LangError");
1612
+ const variantDef = {};
1613
+ const indices = [];
1614
+ if (isLangError$1 && !variants.some((v) => v.index === 0)) {
1615
+ variantDef["_Placeholder"] = _polkadot_api_substrate_bindings._void;
1616
+ indices.push(0);
1617
+ }
1618
+ for (const v of variants) {
1619
+ let fieldCodec;
1620
+ const fields = v.fields ?? [];
1621
+ if (fields.length === 0) fieldCodec = _polkadot_api_substrate_bindings._void;
1622
+ else if (fields.length === 1 && !fields[0]?.name) fieldCodec = buildCodecFromTypeInternal(fields[0].type, types, cache);
1623
+ else {
1624
+ const structDef = {};
1625
+ for (const field of fields) {
1626
+ const fieldName = field.name || `field${fields.indexOf(field)}`;
1627
+ structDef[fieldName] = buildCodecFromTypeInternal(field.type, types, cache);
1628
+ }
1629
+ fieldCodec = (0, _polkadot_api_substrate_bindings.Struct)(structDef);
1630
+ }
1631
+ variantDef[v.name] = fieldCodec;
1632
+ indices.push(v.index);
1633
+ }
1634
+ return (0, _polkadot_api_substrate_bindings.Variant)(variantDef, indices);
1635
+ }
1636
+ function findPrimitiveTypeId(types, primitive) {
1637
+ return types.find((t) => t.type.def.primitive === primitive)?.id ?? -1;
1638
+ }
1639
+ /**
1640
+ * Build a struct codec for message arguments
1641
+ *
1642
+ * @param metadata - The ink contract metadata
1643
+ * @param args - The argument definitions
1644
+ * @returns A struct codec
1645
+ */
1646
+ function buildArgsCodec(metadata, args) {
1647
+ if (args.length === 0) return {
1648
+ enc: () => new Uint8Array(0),
1649
+ dec: () => ({})
1650
+ };
1651
+ const types = metadata.types;
1652
+ const cache = /* @__PURE__ */ new Map();
1653
+ const structDef = {};
1654
+ for (const arg of args) structDef[arg.label] = buildCodecFromTypeInternal(arg.type.type, types, cache);
1655
+ const codec = (0, _polkadot_api_substrate_bindings.Struct)(structDef);
1656
+ return {
1657
+ enc: (value) => codec.enc(value),
1658
+ dec: (data) => codec.dec(data)
1659
+ };
1660
+ }
1661
+
1662
+ //#endregion
1663
+ //#region src/calls.ts
1664
+ /**
1665
+ * Build argument decoders for all messages in the metadata
1666
+ */
1667
+ function buildAllMessageDecodersFromMetadata(metadata) {
1668
+ const decoders = /* @__PURE__ */ new Map();
1669
+ const messages = metadata.spec.messages;
1670
+ for (const message of messages) try {
1671
+ const selectorHex = message.selector.startsWith("0x") ? message.selector.slice(2) : message.selector;
1672
+ const selector = new Uint8Array(selectorHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
1673
+ const argsCodec = buildArgsCodec(metadata, message.args);
1674
+ const decoder = (data) => argsCodec.dec(data);
1675
+ decoders.set(message.label, {
1676
+ selector,
1677
+ decoder
1235
1678
  });
1236
- } };
1679
+ } catch (error) {
1680
+ console.warn(`Failed to build decoder for message "${message.label}":`, error);
1681
+ }
1682
+ return decoders;
1683
+ }
1684
+ /**
1685
+ * Type-safe call parser for a specific contract
1686
+ *
1687
+ * @typeParam S - The storage descriptor type
1688
+ * @typeParam M - The InkCallableDescriptor type (message definitions)
1689
+ * @typeParam C - The constructors descriptor type
1690
+ * @typeParam E - The events Enum type
1691
+ */
1692
+ var ContractCallParser = class {
1693
+ messageDecoders;
1694
+ selectorToLabel;
1695
+ metadata;
1696
+ constructor(descriptor) {
1697
+ if (!descriptor.metadata) throw new Error("Contract descriptor must include metadata");
1698
+ this.metadata = descriptor.metadata;
1699
+ this.messageDecoders = buildAllMessageDecodersFromMetadata(this.metadata);
1700
+ this.selectorToLabel = /* @__PURE__ */ new Map();
1701
+ for (const [label, { selector }] of this.messageDecoders) {
1702
+ const selectorHex = Array.from(selector).map((b) => b.toString(16).padStart(2, "0")).join("");
1703
+ this.selectorToLabel.set(selectorHex, label);
1704
+ }
1705
+ }
1706
+ /**
1707
+ * Parse raw call data into a type-safe contract call
1708
+ *
1709
+ * @param callData - The raw call data (selector + encoded args) or RawContractCall
1710
+ * @returns Parsed call or null if cannot parse
1711
+ *
1712
+ * @example
1713
+ * ```ts
1714
+ * const call = parser.parseCall(callData);
1715
+ * if (call?.type === "PSP22::transfer") {
1716
+ * // call.args is typed as { to: SS58String; value: bigint; data: Uint8Array }
1717
+ * console.log(call.args.to);
1718
+ * }
1719
+ * ```
1720
+ */
1721
+ parseCall(callData) {
1722
+ const data = callData instanceof Uint8Array ? callData : callData.data;
1723
+ const raw = callData instanceof Uint8Array ? { data: callData } : callData;
1724
+ if (data.length < 4) return null;
1725
+ const selector = data.slice(0, 4);
1726
+ const selectorHex = Array.from(selector).map((b) => b.toString(16).padStart(2, "0")).join("");
1727
+ const label = this.selectorToLabel.get(selectorHex);
1728
+ if (!label) return null;
1729
+ const messageInfo = this.messageDecoders.get(label);
1730
+ if (!messageInfo) return null;
1731
+ const argsData = data.slice(4);
1732
+ try {
1733
+ return {
1734
+ type: label,
1735
+ args: messageInfo.decoder(argsData),
1736
+ selector,
1737
+ raw
1738
+ };
1739
+ } catch (error) {
1740
+ console.warn(`Failed to decode call "${label}":`, error);
1741
+ return null;
1742
+ }
1743
+ }
1744
+ /**
1745
+ * Parse multiple calls and optionally filter by message labels
1746
+ *
1747
+ * @param calls - Array of raw call data
1748
+ * @param options - Filter options
1749
+ * @returns Array of parsed calls
1750
+ */
1751
+ parseCalls(calls, options) {
1752
+ const results = [];
1753
+ for (const call of calls) {
1754
+ const parsed = this.parseCall(call);
1755
+ if (!parsed) continue;
1756
+ if (options?.messageLabels && !options.messageLabels.includes(parsed.type)) continue;
1757
+ results.push(parsed);
1758
+ }
1759
+ return results;
1760
+ }
1761
+ /**
1762
+ * Filter calls by specific type with proper type narrowing
1763
+ *
1764
+ * This method provides better type safety than parseCalls with messageLabels
1765
+ * because TypeScript can narrow the return type to only the specified call type.
1766
+ *
1767
+ * @param calls - Array of raw call data
1768
+ * @param label - The message label to filter by
1769
+ * @returns Array of calls narrowed to the specific type
1770
+ *
1771
+ * @example
1772
+ * ```ts
1773
+ * const transfers = parser.filterByType(callDataArray, "PSP22::transfer");
1774
+ *
1775
+ * for (const t of transfers) {
1776
+ * // t.args is fully typed as PSP22::transfer args
1777
+ * console.log(t.args.to, t.args.value);
1778
+ * }
1779
+ * ```
1780
+ */
1781
+ filterByType(calls, label) {
1782
+ return this.parseCalls(calls).filter((c) => c.type === label);
1783
+ }
1784
+ /**
1785
+ * Get information about a message by label
1786
+ */
1787
+ getMessageInfo(label) {
1788
+ const info = this.messageDecoders.get(label);
1789
+ if (!info) return null;
1790
+ const message = this.metadata.spec.messages.find((m) => m.label === label);
1791
+ if (!message) return null;
1792
+ return {
1793
+ label: message.label,
1794
+ selector: info.selector,
1795
+ mutates: message.mutates,
1796
+ payable: message.payable,
1797
+ args: message.args
1798
+ };
1799
+ }
1800
+ /**
1801
+ * Get all available message labels
1802
+ */
1803
+ getMessageLabels() {
1804
+ return Array.from(this.messageDecoders.keys());
1805
+ }
1806
+ /**
1807
+ * Check if a selector matches a specific message
1808
+ */
1809
+ matchesMessage(selector, label) {
1810
+ const info = this.messageDecoders.get(label);
1811
+ if (!info) return false;
1812
+ if (selector.length !== info.selector.length) return false;
1813
+ for (let i = 0; i < selector.length; i++) if (selector[i] !== info.selector[i]) return false;
1814
+ return true;
1815
+ }
1816
+ };
1817
+ /**
1818
+ * Type guard for narrowing call types
1819
+ *
1820
+ * Use this function when you have a `TypedContractCall` and need to
1821
+ * narrow it to a specific call type. This is useful when the type
1822
+ * information is lost (e.g., after serialization or in generic functions).
1823
+ *
1824
+ * @typeParam M - The InkCallableDescriptor type (message definitions)
1825
+ * @typeParam L - The specific message label to check
1826
+ * @param call - The call to check
1827
+ * @param label - The message label to match
1828
+ * @returns True if the call type matches the label
1829
+ *
1830
+ * @example
1831
+ * ```ts
1832
+ * const call = parser.parseCall(callData);
1833
+ *
1834
+ * if (call && isCallType(call, "PSP22::transfer")) {
1835
+ * // TypeScript knows call.args is transfer args
1836
+ * console.log(call.args.to);
1837
+ * console.log(call.args.value);
1838
+ * }
1839
+ * ```
1840
+ */
1841
+ function isCallType(call, label) {
1842
+ return call.type === label;
1843
+ }
1844
+
1845
+ //#endregion
1846
+ //#region src/utils/fees.ts
1847
+ const WEIGHT_REF_TIME_PER_SECOND = 1000000000000n;
1848
+ const WEIGHT_FEE_COEFFICIENT = 1n;
1849
+ /**
1850
+ * Estimate the transaction cost from gas info and storage deposit
1851
+ *
1852
+ * Note: This is an approximation. Actual fees depend on chain configuration
1853
+ * and may vary slightly.
1854
+ *
1855
+ * @param gasInfo - Gas information from contract query
1856
+ * @param storageDeposit - Storage deposit amount (positive for charge, negative for refund)
1857
+ * @returns Estimated cost breakdown
1858
+ *
1859
+ * @example
1860
+ * ```typescript
1861
+ * const result = await contract.query("PSP22::transfer", { origin, args });
1862
+ * if (result.success) {
1863
+ * const cost = estimateTransactionCost(
1864
+ * { gasConsumed: result.gasConsumed, gasRequired: result.gasRequired },
1865
+ * result.storageDeposit
1866
+ * );
1867
+ * console.log(`Estimated cost: ${formatBalance(cost.total, { decimals: 12 })} D9`);
1868
+ * }
1869
+ * ```
1870
+ */
1871
+ function estimateTransactionCost(gasInfo, storageDeposit) {
1872
+ const { gasRequired } = gasInfo;
1873
+ const gasCost = gasRequired.refTime * WEIGHT_FEE_COEFFICIENT / WEIGHT_REF_TIME_PER_SECOND + gasRequired.proofSize;
1874
+ const effectiveStorageDeposit = storageDeposit > 0n ? storageDeposit : 0n;
1875
+ return {
1876
+ gasCost,
1877
+ storageDeposit: effectiveStorageDeposit,
1878
+ total: gasCost + effectiveStorageDeposit
1879
+ };
1880
+ }
1881
+ /**
1882
+ * Format gas information for human-readable display
1883
+ *
1884
+ * @param gasInfo - Gas information from contract query
1885
+ * @returns Formatted strings for ref_time and proof_size
1886
+ *
1887
+ * @example
1888
+ * ```typescript
1889
+ * const formatted = formatGasInfo(result.gasRequired);
1890
+ * console.log(`Gas: refTime=${formatted.refTime}, proofSize=${formatted.proofSize}`);
1891
+ * ```
1892
+ */
1893
+ function formatGasInfo(gasInfo) {
1894
+ return {
1895
+ refTime: formatWeight(gasInfo.refTime),
1896
+ proofSize: formatWeight(gasInfo.proofSize)
1897
+ };
1898
+ }
1899
+ /**
1900
+ * Format a single weight value with appropriate units
1901
+ */
1902
+ function formatWeight(weight) {
1903
+ if (weight >= 1000000000000n) return `${(Number(weight) / 0xe8d4a51000).toFixed(2)}T`;
1904
+ if (weight >= 1000000000n) return `${(Number(weight) / 1e9).toFixed(2)}G`;
1905
+ if (weight >= 1000000n) return `${(Number(weight) / 1e6).toFixed(2)}M`;
1906
+ if (weight >= 1000n) return `${(Number(weight) / 1e3).toFixed(2)}K`;
1907
+ return weight.toString();
1908
+ }
1909
+ /**
1910
+ * Apply a safety margin to gas limits
1911
+ *
1912
+ * It's recommended to add a small margin to gas estimates to account for
1913
+ * slight variations in execution. 10% (multiplier = 1.1) is a common choice.
1914
+ *
1915
+ * @param gas - The gas weight to adjust
1916
+ * @param multiplier - The multiplier to apply (default: 1.1 for 10% margin)
1917
+ * @returns Adjusted gas weight with margin applied
1918
+ *
1919
+ * @example
1920
+ * ```typescript
1921
+ * const result = await contract.query("PSP22::transfer", { origin, args });
1922
+ * if (result.success) {
1923
+ * // Add 10% safety margin
1924
+ * const safeGas = applyGasMargin(result.gasRequired, 1.1);
1925
+ * await contract.send("PSP22::transfer", {
1926
+ * origin,
1927
+ * args,
1928
+ * gasLimit: safeGas,
1929
+ * });
1930
+ * }
1931
+ * ```
1932
+ */
1933
+ function applyGasMargin(gas, multiplier = 1.1) {
1934
+ if (multiplier <= 0) throw new Error("Multiplier must be positive");
1935
+ const basisPoints = BigInt(Math.round(multiplier * 1e4));
1936
+ return {
1937
+ refTime: gas.refTime * basisPoints / 10000n,
1938
+ proofSize: gas.proofSize * basisPoints / 10000n
1939
+ };
1940
+ }
1941
+ /**
1942
+ * Compare two gas weights
1943
+ *
1944
+ * @param a - First gas weight
1945
+ * @param b - Second gas weight
1946
+ * @returns -1 if a < b, 0 if equal, 1 if a > b (compares refTime first, then proofSize)
1947
+ */
1948
+ function compareGasWeight(a, b) {
1949
+ if (a.refTime < b.refTime) return -1;
1950
+ if (a.refTime > b.refTime) return 1;
1951
+ if (a.proofSize < b.proofSize) return -1;
1952
+ if (a.proofSize > b.proofSize) return 1;
1953
+ return 0;
1954
+ }
1955
+ /**
1956
+ * Check if gas weight exceeds a limit
1957
+ *
1958
+ * @param gas - The gas to check
1959
+ * @param limit - The limit to compare against
1960
+ * @returns True if gas exceeds the limit in either dimension
1961
+ */
1962
+ function gasExceedsLimit(gas, limit) {
1963
+ return gas.refTime > limit.refTime || gas.proofSize > limit.proofSize;
1237
1964
  }
1238
1965
 
1239
1966
  //#endregion
1240
1967
  exports.AbortedError = AbortedError;
1968
+ exports.ContractCallParser = ContractCallParser;
1241
1969
  exports.ContractError = ContractError;
1242
1970
  exports.ContractEventParser = ContractEventParser;
1243
1971
  exports.ContractExecutionError = ContractExecutionError;
@@ -1250,25 +1978,35 @@ exports.NetworkError = NetworkError;
1250
1978
  exports.SignerError = SignerError;
1251
1979
  exports.TimeoutError = TimeoutError;
1252
1980
  exports.TransactionError = TransactionError;
1981
+ exports.applyGasMargin = applyGasMargin;
1253
1982
  exports.buildAllEventDecoders = buildAllEventDecoders;
1254
1983
  exports.buildAllMessageDecoders = buildAllMessageDecoders;
1255
1984
  exports.buildEventDecoder = buildEventDecoder;
1256
1985
  exports.buildMessageDecoder = buildMessageDecoder;
1986
+ exports.compareGasWeight = compareGasWeight;
1987
+ exports.createAsciiEventTopic = createAsciiEventTopic;
1257
1988
  exports.createCodecRegistry = createCodecRegistry;
1258
1989
  exports.createContractEventStream = createContractEventStream;
1259
1990
  exports.createD9InkContract = createD9InkContract;
1260
1991
  exports.createD9InkSdk = createD9InkSdk;
1992
+ exports.createMessageBuilder = createMessageBuilder;
1261
1993
  exports.createNativeTransferStream = createNativeTransferStream;
1262
1994
  exports.createPSP22TransferStream = createPSP22TransferStream;
1995
+ exports.createTypedRpc = createTypedRpc;
1263
1996
  exports.decodeContractCallResult = decodeContractCallResult;
1264
1997
  exports.decodeInkValue = decodeInkValue;
1265
1998
  exports.decodeResult = decodeResult;
1266
1999
  exports.encodeCall = encodeCall;
1267
2000
  exports.encodeContractCall = encodeContractCall;
1268
2001
  exports.encodeContractCallWithLimits = encodeContractCallWithLimits;
2002
+ exports.estimateTransactionCost = estimateTransactionCost;
2003
+ exports.formatGasInfo = formatGasInfo;
2004
+ exports.gasExceedsLimit = gasExceedsLimit;
1269
2005
  exports.getEventSignature = getEventSignature;
2006
+ exports.isCallType = isCallType;
1270
2007
  exports.isContractError = isContractError;
1271
2008
  exports.isErrorType = isErrorType;
2009
+ exports.isEventType = isEventType;
1272
2010
  exports.isLangError = isLangError;
1273
2011
  exports.unwrapInkResult = unwrapInkResult;
1274
2012
  //# sourceMappingURL=index.cjs.map