starknet 10.7.0 → 11.0.0-beta.1

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
@@ -609,115 +609,13 @@ ${JSON.stringify(data, null, 2)}`;
609
609
  };
610
610
  var logger = Logger.getInstance();
611
611
 
612
- // src/channel/rpc_0_9_0.ts
613
- var rpc_0_9_0_exports = {};
614
- __export(rpc_0_9_0_exports, {
615
- RpcChannel: () => RpcChannel
612
+ // src/channel/v0_9/index.ts
613
+ var v0_9_exports = {};
614
+ __export(v0_9_exports, {
615
+ RpcChannel: () => RpcChannel,
616
+ SubscriptionChannel: () => SubscriptionChannel2
616
617
  });
617
618
 
618
- // src/types/lib/contract/index.ts
619
- var EntryPointType = {
620
- EXTERNAL: "EXTERNAL",
621
- L1_HANDLER: "L1_HANDLER",
622
- CONSTRUCTOR: "CONSTRUCTOR"
623
- };
624
-
625
- // src/types/calldata.ts
626
- var ValidateType = {
627
- DEPLOY: "DEPLOY",
628
- CALL: "CALL",
629
- INVOKE: "INVOKE"
630
- };
631
- var Uint = {
632
- u8: "core::integer::u8",
633
- u16: "core::integer::u16",
634
- u32: "core::integer::u32",
635
- u64: "core::integer::u64",
636
- u96: "core::integer::u96",
637
- u128: "core::integer::u128",
638
- u256: "core::integer::u256",
639
- // This one is struct
640
- u512: "core::integer::u512"
641
- // This one is struct
642
- };
643
- var Int = {
644
- i8: "core::integer::i8",
645
- i16: "core::integer::i16",
646
- i32: "core::integer::i32",
647
- i64: "core::integer::i64",
648
- i128: "core::integer::i128"
649
- };
650
- var Literal = {
651
- ClassHash: "core::starknet::class_hash::ClassHash",
652
- ContractAddress: "core::starknet::contract_address::ContractAddress",
653
- Secp256k1Point: "core::starknet::secp256k1::Secp256k1Point",
654
- U96: "core::internal::bounded_int::BoundedInt::<0, 79228162514264337593543950335>"
655
- };
656
- var ETH_ADDRESS = "core::starknet::eth_address::EthAddress";
657
- var NON_ZERO_PREFIX = "core::zeroable::NonZero::";
658
-
659
- // src/contract/types/index.type.ts
660
- function isAccount(providerOrAccount) {
661
- return "execute" in providerOrAccount;
662
- }
663
-
664
- // src/types/outsideExecution.ts
665
- var OutsideExecutionTypesV1 = {
666
- StarkNetDomain: [
667
- { name: "name", type: "felt" },
668
- { name: "version", type: "felt" },
669
- { name: "chainId", type: "felt" }
670
- ],
671
- OutsideExecution: [
672
- { name: "caller", type: "felt" },
673
- { name: "nonce", type: "felt" },
674
- { name: "execute_after", type: "felt" },
675
- { name: "execute_before", type: "felt" },
676
- { name: "calls_len", type: "felt" },
677
- { name: "calls", type: "OutsideCall*" }
678
- ],
679
- OutsideCall: [
680
- { name: "to", type: "felt" },
681
- { name: "selector", type: "felt" },
682
- { name: "calldata_len", type: "felt" },
683
- { name: "calldata", type: "felt*" }
684
- ]
685
- };
686
- var OutsideExecutionTypesV2 = {
687
- StarknetDomain: [
688
- // SNIP-12 revision 1 is used, so should be "StarknetDomain", not "StarkNetDomain"
689
- { name: "name", type: "shortstring" },
690
- { name: "version", type: "shortstring" },
691
- // set to 2 in v2
692
- { name: "chainId", type: "shortstring" },
693
- { name: "revision", type: "shortstring" }
694
- ],
695
- OutsideExecution: [
696
- { name: "Caller", type: "ContractAddress" },
697
- { name: "Nonce", type: "felt" },
698
- { name: "Execute After", type: "u128" },
699
- { name: "Execute Before", type: "u128" },
700
- { name: "Calls", type: "Call*" }
701
- ],
702
- Call: [
703
- { name: "To", type: "ContractAddress" },
704
- { name: "Selector", type: "selector" },
705
- { name: "Calldata", type: "felt*" }
706
- ]
707
- };
708
- var OutsideExecutionVersion = {
709
- UNSUPPORTED: "0",
710
- V1: "1",
711
- V2: "2"
712
- };
713
-
714
- // src/utils/assert.ts
715
- function assert(condition, message) {
716
- if (!condition) {
717
- throw new Error(message || "Assertion failure");
718
- }
719
- }
720
-
721
619
  // src/utils/json.ts
722
620
  var json_exports = {};
723
621
  __export(json_exports, {
@@ -735,87 +633,114 @@ var parse2 = (str) => json.parse(String(str), void 0, parseIntAsNumberOrBigInt);
735
633
  var parseAlwaysAsBig = (str) => json.parse(String(str), void 0, json.parseNumberAndBigInt);
736
634
  var stringify2 = (value, replacer, space, numberStringifiers) => json.stringify(value, replacer, space, numberStringifiers);
737
635
 
738
- // src/utils/batch/index.ts
739
- var BatchClient = class {
740
- nodeUrl;
741
- headers;
742
- interval;
743
- requestId = 0;
744
- pendingRequests = {};
745
- batchPromises = {};
746
- delayTimer;
747
- delayPromise;
748
- delayPromiseResolve;
749
- baseFetch;
750
- rpcMethods;
751
- constructor(options) {
752
- this.nodeUrl = options.nodeUrl;
753
- this.headers = options.headers;
754
- this.interval = options.interval;
755
- this.baseFetch = options.baseFetch;
756
- this.rpcMethods = options.rpcMethods;
757
- }
758
- async wait() {
759
- if (!this.delayPromise || !this.delayPromiseResolve) {
760
- this.delayPromise = new Promise((resolve) => {
761
- this.delayPromiseResolve = resolve;
762
- });
763
- }
764
- if (this.delayTimer) {
765
- clearTimeout(this.delayTimer);
766
- this.delayTimer = void 0;
767
- }
768
- this.delayTimer = setTimeout(() => {
769
- if (this.delayPromiseResolve) {
770
- this.delayPromiseResolve();
771
- this.delayPromise = void 0;
772
- this.delayPromiseResolve = void 0;
773
- }
774
- }, this.interval);
775
- return this.delayPromise;
636
+ // src/utils/errors/rpc.ts
637
+ var errorCodes = {
638
+ FAILED_TO_RECEIVE_TXN: 1,
639
+ NO_TRACE_AVAILABLE: 10,
640
+ CONTRACT_NOT_FOUND: 20,
641
+ ENTRYPOINT_NOT_FOUND: 21,
642
+ BLOCK_NOT_FOUND: 24,
643
+ INVALID_TXN_INDEX: 27,
644
+ CLASS_HASH_NOT_FOUND: 28,
645
+ TXN_HASH_NOT_FOUND: 29,
646
+ PAGE_SIZE_TOO_BIG: 31,
647
+ NO_BLOCKS: 32,
648
+ INVALID_CONTINUATION_TOKEN: 33,
649
+ TOO_MANY_KEYS_IN_FILTER: 34,
650
+ CONTRACT_ERROR: 40,
651
+ TRANSACTION_EXECUTION_ERROR: 41,
652
+ STORAGE_PROOF_NOT_SUPPORTED: 42,
653
+ CLASS_ALREADY_DECLARED: 51,
654
+ INVALID_TRANSACTION_NONCE: 52,
655
+ INSUFFICIENT_RESOURCES_FOR_VALIDATE: 53,
656
+ INSUFFICIENT_ACCOUNT_BALANCE: 54,
657
+ VALIDATION_FAILURE: 55,
658
+ COMPILATION_FAILED: 56,
659
+ CONTRACT_CLASS_SIZE_IS_TOO_LARGE: 57,
660
+ NON_ACCOUNT: 58,
661
+ DUPLICATE_TX: 59,
662
+ COMPILED_CLASS_HASH_MISMATCH: 60,
663
+ UNSUPPORTED_TX_VERSION: 61,
664
+ UNSUPPORTED_CONTRACT_CLASS_VERSION: 62,
665
+ UNEXPECTED_ERROR: 63,
666
+ REPLACEMENT_TRANSACTION_UNDERPRICED: 64,
667
+ FEE_BELOW_MINIMUM: 65,
668
+ INVALID_SUBSCRIPTION_ID: 66,
669
+ TOO_MANY_ADDRESSES_IN_FILTER: 67,
670
+ TOO_MANY_BLOCKS_BACK: 68,
671
+ COMPILATION_ERROR: 100,
672
+ //
673
+ INVALID_ADDRESS: 150,
674
+ TOKEN_NOT_SUPPORTED: 151,
675
+ INVALID_SIGNATURE: 153,
676
+ MAX_AMOUNT_TOO_LOW: 154,
677
+ CLASS_HASH_NOT_SUPPORTED: 155,
678
+ PAYMASTER_TRANSACTION_EXECUTION_ERROR: 156,
679
+ INVALID_TIME_BOUNDS: 157,
680
+ INVALID_DEPLOYMENT_DATA: 158,
681
+ INVALID_CLASS_HASH: 159,
682
+ INVALID_ID: 160,
683
+ UNKNOWN_ERROR: 163
684
+ };
685
+ var rpc_default = errorCodes;
686
+
687
+ // src/utils/errors/index.ts
688
+ function fixStack(target, fn = target.constructor) {
689
+ const { captureStackTrace } = Error;
690
+ captureStackTrace && captureStackTrace(target, fn);
691
+ }
692
+ function fixProto(target, prototype) {
693
+ const { setPrototypeOf } = Object;
694
+ setPrototypeOf ? setPrototypeOf(target, prototype) : target.__proto__ = prototype;
695
+ }
696
+ var CustomError = class extends Error {
697
+ name;
698
+ constructor(message) {
699
+ super(message);
700
+ Object.defineProperty(this, "name", {
701
+ value: new.target.name,
702
+ enumerable: false,
703
+ configurable: true
704
+ });
705
+ fixProto(this, new.target.prototype);
706
+ fixStack(this);
776
707
  }
777
- addPendingRequest(method, params, id) {
778
- const request = {
779
- id: id ?? `batched_${this.requestId += 1}`,
780
- jsonrpc: "2.0",
781
- method: method.description || String(method),
782
- params: params ?? void 0
783
- };
784
- this.pendingRequests[request.id] = request;
785
- return request.id;
708
+ };
709
+ var LibraryError = class extends CustomError {
710
+ };
711
+ var RpcError = class extends LibraryError {
712
+ constructor(baseError, method, params) {
713
+ super(`RPC: ${method} with params ${stringify2(params, null, 2)}
714
+
715
+ ${baseError.code}: ${baseError.message}: ${stringify2(baseError.data)}`);
716
+ this.baseError = baseError;
717
+ this.request = { method, params };
786
718
  }
787
- async sendBatch(requests) {
788
- const raw = await this.baseFetch(this.nodeUrl, {
789
- method: "POST",
790
- body: stringify2(requests),
791
- headers: this.headers
792
- });
793
- return raw.json();
719
+ request;
720
+ get code() {
721
+ return this.baseError.code;
794
722
  }
795
723
  /**
796
- * Automatically batches and fetches JSON-RPC calls in a single request.
797
- * @param method Method to call
798
- * @param params Method parameters
799
- * @param id JSON-RPC Request ID
800
- * @returns JSON-RPC Response
724
+ * Verifies the underlying RPC error, also serves as a type guard for the _baseError_ property
725
+ * @example
726
+ * ```typescript
727
+ * SomeError.isType('UNEXPECTED_ERROR');
728
+ * ```
801
729
  */
802
- async fetch(method, params, id) {
803
- const requestId = this.addPendingRequest(method, params, id);
804
- await this.wait();
805
- const requests = this.pendingRequests;
806
- this.pendingRequests = {};
807
- if (!this.batchPromises[requestId]) {
808
- const promise = this.sendBatch(Object.values(requests));
809
- Object.keys(requests).forEach((key) => {
810
- this.batchPromises[key] = promise;
811
- });
812
- }
813
- const results = await this.batchPromises[requestId];
814
- delete this.batchPromises[requestId];
815
- const result = results.find((res) => res.id === requestId);
816
- if (!result)
817
- throw new Error(`Couldn't find the result for the request. Method: ${String(method)}`);
818
- return result;
730
+ isType(typeName) {
731
+ return rpc_default[typeName] === this.code;
732
+ }
733
+ };
734
+ var TimeoutError = class extends LibraryError {
735
+ constructor(message) {
736
+ super(message);
737
+ this.name = "TimeoutError";
738
+ }
739
+ };
740
+ var WebSocketNotConnectedError = class extends LibraryError {
741
+ constructor(message) {
742
+ super(message);
743
+ this.name = "WebSocketNotConnectedError";
819
744
  }
820
745
  };
821
746
 
@@ -846,8 +771,16 @@ __export(num_exports, {
846
771
  toStorageKey: () => toStorageKey,
847
772
  tryToBigInt: () => tryToBigInt
848
773
  });
849
- import { hexToBytes as hexToBytesNoble } from "@noble/curves/abstract/utils";
850
- import { sha256 } from "@noble/hashes/sha256";
774
+ import { hexToBytes as hexToBytesNoble } from "@noble/curves/utils.js";
775
+ import { sha256 } from "@noble/hashes/sha2.js";
776
+ import { utf8ToBytes } from "@noble/hashes/utils.js";
777
+
778
+ // src/utils/assert.ts
779
+ function assert(condition, message) {
780
+ if (!condition) {
781
+ throw new Error(message || "Assertion failure");
782
+ }
783
+ }
851
784
 
852
785
  // src/utils/typed.ts
853
786
  var isUndefined = (value) => {
@@ -961,7 +894,7 @@ function addPercent(number, percent) {
961
894
  }
962
895
  function stringToSha256ToArrayBuff4(str) {
963
896
  const int31 = (n) => Number(n & MASK_31);
964
- const result = int31(BigInt(addHexPrefix(buf2hex(sha256(str)))));
897
+ const result = int31(BigInt(addHexPrefix(buf2hex(sha256(utf8ToBytes(str))))));
965
898
  return hexToBytes(toHex(result));
966
899
  }
967
900
  function isBigNumberish(input) {
@@ -973,6 +906,155 @@ function getNext(iterator) {
973
906
  return it.value;
974
907
  }
975
908
 
909
+ // src/utils/provider.ts
910
+ var provider_exports = {};
911
+ __export(provider_exports, {
912
+ Block: () => Block,
913
+ createSierraContractClass: () => createSierraContractClass,
914
+ extractAbi: () => extractAbi,
915
+ getDefaultNodeUrl: () => getDefaultNodeUrl,
916
+ getDefaultNodes: () => getDefaultNodes,
917
+ getSupportedRpcVersions: () => getSupportedRpcVersions,
918
+ parseContract: () => parseContract,
919
+ validBlockTags: () => validBlockTags,
920
+ wait: () => wait
921
+ });
922
+
923
+ // src/types/lib/contract/index.ts
924
+ var EntryPointType = {
925
+ EXTERNAL: "EXTERNAL",
926
+ L1_HANDLER: "L1_HANDLER",
927
+ CONSTRUCTOR: "CONSTRUCTOR"
928
+ };
929
+
930
+ // src/types/calldata.ts
931
+ var ValidateType = {
932
+ DEPLOY: "DEPLOY",
933
+ CALL: "CALL",
934
+ INVOKE: "INVOKE"
935
+ };
936
+ var Uint = {
937
+ u8: "core::integer::u8",
938
+ u16: "core::integer::u16",
939
+ u32: "core::integer::u32",
940
+ u64: "core::integer::u64",
941
+ u96: "core::integer::u96",
942
+ u128: "core::integer::u128",
943
+ u256: "core::integer::u256",
944
+ // This one is struct
945
+ u512: "core::integer::u512"
946
+ // This one is struct
947
+ };
948
+ var Int = {
949
+ i8: "core::integer::i8",
950
+ i16: "core::integer::i16",
951
+ i32: "core::integer::i32",
952
+ i64: "core::integer::i64",
953
+ i128: "core::integer::i128"
954
+ };
955
+ var Literal = {
956
+ ClassHash: "core::starknet::class_hash::ClassHash",
957
+ ContractAddress: "core::starknet::contract_address::ContractAddress",
958
+ Secp256k1Point: "core::starknet::secp256k1::Secp256k1Point",
959
+ U96: "core::internal::bounded_int::BoundedInt::<0, 79228162514264337593543950335>"
960
+ };
961
+ var ETH_ADDRESS = "core::starknet::eth_address::EthAddress";
962
+ var NON_ZERO_PREFIX = "core::zeroable::NonZero::";
963
+
964
+ // src/contract/types/index.type.ts
965
+ function isAccount(providerOrAccount) {
966
+ return "execute" in providerOrAccount;
967
+ }
968
+
969
+ // src/types/outsideExecution.ts
970
+ var OutsideExecutionTypesV1 = {
971
+ StarkNetDomain: [
972
+ { name: "name", type: "felt" },
973
+ { name: "version", type: "felt" },
974
+ { name: "chainId", type: "felt" }
975
+ ],
976
+ OutsideExecution: [
977
+ { name: "caller", type: "felt" },
978
+ { name: "nonce", type: "felt" },
979
+ { name: "execute_after", type: "felt" },
980
+ { name: "execute_before", type: "felt" },
981
+ { name: "calls_len", type: "felt" },
982
+ { name: "calls", type: "OutsideCall*" }
983
+ ],
984
+ OutsideCall: [
985
+ { name: "to", type: "felt" },
986
+ { name: "selector", type: "felt" },
987
+ { name: "calldata_len", type: "felt" },
988
+ { name: "calldata", type: "felt*" }
989
+ ]
990
+ };
991
+ var OutsideExecutionTypesV2 = {
992
+ StarknetDomain: [
993
+ // SNIP-12 revision 1 is used, so should be "StarknetDomain", not "StarkNetDomain"
994
+ { name: "name", type: "shortstring" },
995
+ { name: "version", type: "shortstring" },
996
+ // set to 2 in v2
997
+ { name: "chainId", type: "shortstring" },
998
+ { name: "revision", type: "shortstring" }
999
+ ],
1000
+ OutsideExecution: [
1001
+ { name: "Caller", type: "ContractAddress" },
1002
+ { name: "Nonce", type: "felt" },
1003
+ { name: "Execute After", type: "u128" },
1004
+ { name: "Execute Before", type: "u128" },
1005
+ { name: "Calls", type: "Call*" }
1006
+ ],
1007
+ Call: [
1008
+ { name: "To", type: "ContractAddress" },
1009
+ { name: "Selector", type: "selector" },
1010
+ { name: "Calldata", type: "felt*" }
1011
+ ]
1012
+ };
1013
+ var OutsideExecutionVersion = {
1014
+ UNSUPPORTED: "0",
1015
+ V1: "1",
1016
+ V2: "2"
1017
+ };
1018
+
1019
+ // src/utils/hash/index.ts
1020
+ var hash_exports = {};
1021
+ __export(hash_exports, {
1022
+ COMPILED_CLASS_VERSION: () => COMPILED_CLASS_VERSION,
1023
+ blake2sHashMany: () => blake2sHashMany,
1024
+ calculateContractAddressFromHash: () => calculateContractAddressFromHash,
1025
+ calculateDeclareTransactionHash: () => calculateDeclareTransactionHash3,
1026
+ calculateDeployAccountTransactionHash: () => calculateDeployAccountTransactionHash3,
1027
+ calculateInvokeTransactionHash: () => calculateInvokeTransactionHash2,
1028
+ calculateL2MessageTxHash: () => calculateL2MessageTxHash,
1029
+ computeCompiledClassHash: () => computeCompiledClassHash,
1030
+ computeCompiledClassHashBlake: () => computeCompiledClassHashBlake,
1031
+ computeCompiledClassHashPoseidon: () => computeCompiledClassHashPoseidon,
1032
+ computeContractClassHash: () => computeContractClassHash,
1033
+ computeHashOnElements: () => computeHashOnElements2,
1034
+ computeHintedClassHash: () => computeHintedClassHash,
1035
+ computeLegacyContractClassHash: () => computeLegacyContractClassHash,
1036
+ computePedersenHash: () => computePedersenHash,
1037
+ computePedersenHashOnElements: () => computePedersenHashOnElements,
1038
+ computePoseidonHash: () => computePoseidonHash,
1039
+ computePoseidonHashOnElements: () => computePoseidonHashOnElements,
1040
+ computeSierraContractClassHash: () => computeSierraContractClassHash,
1041
+ encodeBuiltins: () => encodeBuiltins,
1042
+ flattenEntryPointData: () => flattenEntryPointData,
1043
+ formatSpaces: () => formatSpaces,
1044
+ getL1MessageHash: () => getL1MessageHash,
1045
+ getL2MessageHash: () => getL2MessageHash,
1046
+ getSelector: () => getSelector,
1047
+ getSelectorFromName: () => getSelectorFromName,
1048
+ hashByteCodeSegments: () => hashByteCodeSegments,
1049
+ hashByteCodeSegmentsBlake: () => hashByteCodeSegmentsBlake,
1050
+ keccakBn: () => keccakBn,
1051
+ nullSkipReplacer: () => nullSkipReplacer,
1052
+ poseidon: () => poseidon,
1053
+ solidityUint256PackedKeccak256: () => solidityUint256PackedKeccak256,
1054
+ starknetKeccak: () => starknetKeccak
1055
+ });
1056
+ import * as poseidon from "@noble/curves/abstract/poseidon.js";
1057
+
976
1058
  // src/utils/hash/selector.ts
977
1059
  var selector_exports = {};
978
1060
  __export(selector_exports, {
@@ -985,8 +1067,8 @@ __export(selector_exports, {
985
1067
  starknetKeccak: () => starknetKeccak
986
1068
  });
987
1069
  import { keccak } from "@scure/starknet";
988
- import { keccak_256 } from "@noble/hashes/sha3";
989
- import { bytesToHex } from "@noble/curves/abstract/utils";
1070
+ import { keccak_256 } from "@noble/hashes/sha3.js";
1071
+ import { bytesToHex } from "@noble/curves/utils.js";
990
1072
  function keccakBn(value) {
991
1073
  const hexWithoutPrefix = removeHexPrefix(toHex(BigInt(value)));
992
1074
  const evenHex = hexWithoutPrefix.length % 2 === 0 ? hexWithoutPrefix : `0${hexWithoutPrefix}`;
@@ -1031,6 +1113,21 @@ function getL1MessageHash(fromL2Address, toL1Address, payload) {
1031
1113
  return solidityUint256PackedKeccak256([fromL2Address, toL1Address, payload.length, ...payload]);
1032
1114
  }
1033
1115
 
1116
+ // src/utils/hash/transactionHash/v3.ts
1117
+ var v3_exports = {};
1118
+ __export(v3_exports, {
1119
+ calculateDeclareTransactionHash: () => calculateDeclareTransactionHash,
1120
+ calculateDeployAccountTransactionHash: () => calculateDeployAccountTransactionHash,
1121
+ calculateInvokeTransactionHash: () => calculateInvokeTransactionHash,
1122
+ calculateTransactionHashCommon: () => calculateTransactionHashCommon,
1123
+ encodeDataResourceBoundsL1: () => encodeDataResourceBoundsL1,
1124
+ encodeResourceBoundsL1: () => encodeResourceBoundsL1,
1125
+ encodeResourceBoundsL2: () => encodeResourceBoundsL2,
1126
+ hashDAMode: () => hashDAMode,
1127
+ hashFeeFieldV3B3: () => hashFeeFieldV3B3
1128
+ });
1129
+ import { poseidonHashMany } from "@scure/starknet";
1130
+
1034
1131
  // src/utils/shortString.ts
1035
1132
  var shortString_exports = {};
1036
1133
  __export(shortString_exports, {
@@ -1078,142 +1175,393 @@ function decodeShortString(str) {
1078
1175
  throw new Error(`${str} is not Hex or decimal`);
1079
1176
  }
1080
1177
 
1081
- // src/utils/calldata/byteArray.ts
1082
- var byteArray_exports = {};
1083
- __export(byteArray_exports, {
1084
- byteArrayFromString: () => byteArrayFromString,
1085
- stringFromByteArray: () => stringFromByteArray
1086
- });
1087
- function stringFromByteArray(myByteArray) {
1088
- const pending_word = BigInt(myByteArray.pending_word) === 0n ? "" : decodeShortString(toHex(myByteArray.pending_word));
1089
- return myByteArray.data.reduce((cumuledString, encodedString) => {
1090
- const add = BigInt(encodedString) === 0n ? "" : decodeShortString(toHex(encodedString));
1091
- return cumuledString + add;
1092
- }, "") + pending_word;
1178
+ // src/utils/hash/transactionHash/v3.ts
1179
+ var AToBI = (array) => array.map((it) => BigInt(it));
1180
+ var DATA_AVAILABILITY_MODE_BITS = 32n;
1181
+ var MAX_AMOUNT_BITS = 64n;
1182
+ var MAX_PRICE_PER_UNIT_BITS = 128n;
1183
+ var RESOURCE_VALUE_OFFSET = MAX_AMOUNT_BITS + MAX_PRICE_PER_UNIT_BITS;
1184
+ var L1_GAS_NAME = BigInt(encodeShortString("L1_GAS"));
1185
+ var L2_GAS_NAME = BigInt(encodeShortString("L2_GAS"));
1186
+ var L1_DATA_GAS_NAME = BigInt(encodeShortString("L1_DATA"));
1187
+ function hashDAMode(nonceDAMode, feeDAMode) {
1188
+ return (BigInt(nonceDAMode) << DATA_AVAILABILITY_MODE_BITS) + BigInt(feeDAMode);
1093
1189
  }
1094
- function byteArrayFromString(targetString) {
1095
- const shortStrings = splitLongString(targetString);
1096
- const remainder = shortStrings[shortStrings.length - 1];
1097
- const shortStringsEncoded = shortStrings.map(encodeShortString);
1098
- const [pendingWord, pendingWordLength] = remainder === void 0 || remainder.length === 31 ? ["0x00", 0] : [shortStringsEncoded.pop(), remainder.length];
1099
- return {
1100
- data: shortStringsEncoded.length === 0 ? [] : shortStringsEncoded,
1101
- pending_word: pendingWord,
1102
- pending_word_len: pendingWordLength
1103
- };
1190
+ function encodeResourceBoundsL1(bounds) {
1191
+ return (L1_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.l1_gas.max_amount << MAX_PRICE_PER_UNIT_BITS) + bounds.l1_gas.max_price_per_unit;
1104
1192
  }
1105
-
1106
- // src/utils/calldata/cairo.ts
1107
- var cairo_exports = {};
1108
- __export(cairo_exports, {
1109
- felt: () => felt,
1110
- getAbiContractVersion: () => getAbiContractVersion,
1111
- getArrayType: () => getArrayType,
1112
- isCairo1Abi: () => isCairo1Abi,
1113
- isCairo1Type: () => isCairo1Type,
1114
- isLen: () => isLen,
1115
- isTypeArray: () => isTypeArray,
1116
- isTypeBool: () => isTypeBool,
1117
- isTypeContractAddress: () => isTypeContractAddress,
1118
- isTypeEnum: () => isTypeEnum,
1119
- isTypeEthAddress: () => isTypeEthAddress,
1120
- isTypeFelt: () => isTypeFelt,
1121
- isTypeInt: () => isTypeInt,
1122
- isTypeLiteral: () => isTypeLiteral,
1123
- isTypeNamedTuple: () => isTypeNamedTuple,
1124
- isTypeNonZero: () => isTypeNonZero,
1125
- isTypeOption: () => isTypeOption,
1126
- isTypeResult: () => isTypeResult,
1127
- isTypeSecp256k1Point: () => isTypeSecp256k1Point,
1128
- isTypeStruct: () => isTypeStruct,
1129
- isTypeTuple: () => isTypeTuple,
1130
- isTypeU96: () => isTypeU96,
1131
- isTypeUint: () => isTypeUint,
1132
- isTypeUint256: () => isTypeUint256,
1133
- tuple: () => tuple,
1134
- uint256: () => uint256,
1135
- uint512: () => uint512
1193
+ function encodeResourceBoundsL2(bounds) {
1194
+ return (L2_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.l2_gas.max_amount << MAX_PRICE_PER_UNIT_BITS) + bounds.l2_gas.max_price_per_unit;
1195
+ }
1196
+ function encodeDataResourceBoundsL1(bounds) {
1197
+ return (L1_DATA_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.l1_data_gas.max_amount << MAX_PRICE_PER_UNIT_BITS) + bounds.l1_data_gas.max_price_per_unit;
1198
+ }
1199
+ function hashFeeFieldV3B3(tip, bounds) {
1200
+ const L1Bound = encodeResourceBoundsL1(bounds);
1201
+ const L2Bound = encodeResourceBoundsL2(bounds);
1202
+ const L1Data = encodeDataResourceBoundsL1(bounds);
1203
+ return poseidonHashMany([BigInt(tip), L1Bound, L2Bound, L1Data]);
1204
+ }
1205
+ function calculateTransactionHashCommon(txHashPrefix, version, senderAddress, chainId, nonce, tip, paymasterData, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, additionalData = []) {
1206
+ const feeFieldHash = hashFeeFieldV3B3(tip, resourceBounds);
1207
+ const dAModeHash = hashDAMode(nonceDataAvailabilityMode, feeDataAvailabilityMode);
1208
+ const dataToHash = AToBI([
1209
+ txHashPrefix,
1210
+ version,
1211
+ senderAddress,
1212
+ feeFieldHash,
1213
+ poseidonHashMany(AToBI(paymasterData)),
1214
+ chainId,
1215
+ nonce,
1216
+ dAModeHash,
1217
+ ...AToBI(additionalData)
1218
+ ]);
1219
+ return toHex(poseidonHashMany(dataToHash));
1220
+ }
1221
+ function calculateDeployAccountTransactionHash(contractAddress, classHash, compiledConstructorCalldata, salt, version, chainId, nonce, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, tip, paymasterData) {
1222
+ return calculateTransactionHashCommon(
1223
+ _TransactionHashPrefix.DEPLOY_ACCOUNT,
1224
+ version,
1225
+ contractAddress,
1226
+ chainId,
1227
+ nonce,
1228
+ tip,
1229
+ paymasterData,
1230
+ nonceDataAvailabilityMode,
1231
+ feeDataAvailabilityMode,
1232
+ resourceBounds,
1233
+ [poseidonHashMany(AToBI(compiledConstructorCalldata)), classHash, salt]
1234
+ );
1235
+ }
1236
+ function calculateDeclareTransactionHash(classHash, compiledClassHash, senderAddress, version, chainId, nonce, accountDeploymentData, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, tip, paymasterData) {
1237
+ return calculateTransactionHashCommon(
1238
+ _TransactionHashPrefix.DECLARE,
1239
+ version,
1240
+ senderAddress,
1241
+ chainId,
1242
+ nonce,
1243
+ tip,
1244
+ AToBI(paymasterData),
1245
+ nonceDataAvailabilityMode,
1246
+ feeDataAvailabilityMode,
1247
+ resourceBounds,
1248
+ [poseidonHashMany(AToBI(accountDeploymentData)), classHash, compiledClassHash]
1249
+ );
1250
+ }
1251
+ function calculateInvokeTransactionHash(senderAddress, version, compiledCalldata, chainId, nonce, accountDeploymentData, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, tip, paymasterData, proofFacts) {
1252
+ const proofFactsAdditionalData = proofFacts?.length ? [poseidonHashMany(AToBI(proofFacts))] : [];
1253
+ return calculateTransactionHashCommon(
1254
+ _TransactionHashPrefix.INVOKE,
1255
+ version,
1256
+ senderAddress,
1257
+ chainId,
1258
+ nonce,
1259
+ tip,
1260
+ paymasterData,
1261
+ nonceDataAvailabilityMode,
1262
+ feeDataAvailabilityMode,
1263
+ resourceBounds,
1264
+ [
1265
+ poseidonHashMany(AToBI(accountDeploymentData)),
1266
+ poseidonHashMany(AToBI(compiledCalldata)),
1267
+ ...proofFactsAdditionalData
1268
+ ]
1269
+ );
1270
+ }
1271
+
1272
+ // src/utils/hash/transactionHash/v2.ts
1273
+ var v2_exports = {};
1274
+ __export(v2_exports, {
1275
+ calculateDeclareTransactionHash: () => calculateDeclareTransactionHash2,
1276
+ calculateDeployAccountTransactionHash: () => calculateDeployAccountTransactionHash2,
1277
+ calculateL2MessageTxHash: () => calculateL2MessageTxHash,
1278
+ calculateTransactionHash: () => calculateTransactionHash,
1279
+ calculateTransactionHashCommon: () => calculateTransactionHashCommon2,
1280
+ computeHashOnElements: () => computeHashOnElements
1136
1281
  });
1137
1282
 
1138
- // src/utils/helpers.ts
1139
- function addCompiledFlag(compiled) {
1140
- Object.defineProperty(compiled, "__compiled__", {
1141
- enumerable: false,
1142
- writable: false,
1143
- value: true
1144
- });
1145
- return compiled;
1283
+ // src/utils/ec.ts
1284
+ var ec_exports = {};
1285
+ __export(ec_exports, {
1286
+ starkCurve: () => starkCurve,
1287
+ weierstrass: () => weierstrass
1288
+ });
1289
+ import * as starkCurve from "@scure/starknet";
1290
+ import * as weierstrass from "@noble/curves/abstract/weierstrass.js";
1291
+
1292
+ // src/utils/hash/transactionHash/v2.ts
1293
+ function computeHashOnElements(data) {
1294
+ return [...data, data.length].reduce((x, y) => starkCurve.pedersen(toBigInt(x), toBigInt(y)), 0).toString();
1295
+ }
1296
+ function calculateTransactionHashCommon2(txHashPrefix, version, contractAddress, entryPointSelector, calldata, maxFee, chainId, additionalData = []) {
1297
+ const calldataHash = computeHashOnElements(calldata);
1298
+ const dataToHash = [
1299
+ txHashPrefix,
1300
+ version,
1301
+ contractAddress,
1302
+ entryPointSelector,
1303
+ calldataHash,
1304
+ maxFee,
1305
+ chainId,
1306
+ ...additionalData
1307
+ ];
1308
+ return computeHashOnElements(dataToHash);
1309
+ }
1310
+ function calculateDeclareTransactionHash2(classHash, senderAddress, version, maxFee, chainId, nonce, compiledClassHash) {
1311
+ return calculateTransactionHashCommon2(
1312
+ _TransactionHashPrefix.DECLARE,
1313
+ version,
1314
+ senderAddress,
1315
+ 0,
1316
+ [classHash],
1317
+ maxFee,
1318
+ chainId,
1319
+ [nonce, ...compiledClassHash ? [compiledClassHash] : []]
1320
+ );
1321
+ }
1322
+ function calculateDeployAccountTransactionHash2(contractAddress, classHash, constructorCalldata, salt, version, maxFee, chainId, nonce) {
1323
+ const calldata = [classHash, salt, ...constructorCalldata];
1324
+ return calculateTransactionHashCommon2(
1325
+ _TransactionHashPrefix.DEPLOY_ACCOUNT,
1326
+ version,
1327
+ contractAddress,
1328
+ 0,
1329
+ calldata,
1330
+ maxFee,
1331
+ chainId,
1332
+ [nonce]
1333
+ );
1334
+ }
1335
+ function calculateTransactionHash(contractAddress, version, calldata, maxFee, chainId, nonce) {
1336
+ return calculateTransactionHashCommon2(
1337
+ _TransactionHashPrefix.INVOKE,
1338
+ version,
1339
+ contractAddress,
1340
+ 0,
1341
+ calldata,
1342
+ maxFee,
1343
+ chainId,
1344
+ [nonce]
1345
+ );
1346
+ }
1347
+ function calculateL2MessageTxHash(l1FromAddress, l2ToAddress, l2Selector, l2Calldata, l2ChainId, l1Nonce) {
1348
+ const payload = [l1FromAddress, ...l2Calldata];
1349
+ return calculateTransactionHashCommon2(
1350
+ _TransactionHashPrefix.L1_HANDLER,
1351
+ 0,
1352
+ l2ToAddress,
1353
+ getSelector(l2Selector),
1354
+ payload,
1355
+ 0,
1356
+ l2ChainId,
1357
+ [l1Nonce]
1358
+ );
1146
1359
  }
1147
1360
 
1148
- // src/utils/cairoDataTypes/felt.ts
1149
- var CairoFelt252 = class _CairoFelt252 {
1150
- /**
1151
- * byte representation of the felt252
1152
- */
1153
- data;
1154
- static abiSelector = "core::felt252";
1155
- constructor(data) {
1156
- _CairoFelt252.validate(data);
1157
- const processedData = _CairoFelt252.__processData(data);
1158
- this.data = processedData.subarray(processedData.findIndex((x) => x > 0));
1159
- }
1160
- static __processData(data) {
1161
- if (isString(data)) {
1162
- return stringToUint8Array(data);
1163
- }
1164
- if (isBigInt(data)) {
1165
- return bigIntToUint8Array(data);
1166
- }
1167
- if (Number.isInteger(data)) {
1168
- return bigIntToUint8Array(BigInt(data));
1169
- }
1170
- if (isBoolean(data)) {
1171
- return bigIntToUint8Array(BigInt(data ? 1 : 0));
1172
- }
1173
- throw new Error(`${data} can't be computed by felt()`);
1174
- }
1175
- toBigInt() {
1176
- return uint8ArrayToBigInt(this.data);
1177
- }
1178
- decodeUtf8() {
1179
- return new TextDecoder().decode(this.data);
1180
- }
1181
- toHexString() {
1182
- return addHexPrefix(this.toBigInt().toString(16));
1183
- }
1184
- toApiRequest() {
1185
- return addCompiledFlag([BigInt(this.toHexString()).toString()]);
1186
- }
1187
- static assertRange(val) {
1188
- assert(val >= 0n && val < PRIME, `Value ${val} is out of felt252 range [0, ${PRIME})`);
1189
- }
1190
- static validate(data) {
1191
- assert(data !== null, "null value is not allowed for felt252");
1192
- assert(data !== void 0, "undefined value is not allowed for felt252");
1193
- assert(
1194
- isString(data) || isNumber(data) || isBigInt(data) || isBoolean(data),
1195
- `Unsupported data type '${typeof data}' for felt252. Expected string, number, bigint, or boolean`
1361
+ // src/utils/hash/transactionHash/index.ts
1362
+ function isV3InvokeTx(args) {
1363
+ return [api_exports.ETransactionVersion.V3, api_exports.ETransactionVersion.F3].includes(args.version);
1364
+ }
1365
+ function calculateInvokeTransactionHash2(args) {
1366
+ if (isV3InvokeTx(args)) {
1367
+ return calculateInvokeTransactionHash(
1368
+ args.senderAddress,
1369
+ args.version,
1370
+ args.compiledCalldata,
1371
+ args.chainId,
1372
+ args.nonce,
1373
+ args.accountDeploymentData,
1374
+ args.nonceDataAvailabilityMode,
1375
+ args.feeDataAvailabilityMode,
1376
+ args.resourceBounds,
1377
+ args.tip,
1378
+ args.paymasterData,
1379
+ args.proofFacts
1196
1380
  );
1197
- const value = _CairoFelt252.__processData(data);
1198
- const bn = uint8ArrayToBigInt(value);
1199
- _CairoFelt252.assertRange(bn);
1200
1381
  }
1201
- static is(data) {
1202
- try {
1203
- _CairoFelt252.validate(data);
1204
- return true;
1205
- } catch {
1206
- return false;
1207
- }
1208
- }
1209
- static isAbiType(abiType) {
1210
- return abiType === _CairoFelt252.abiSelector;
1211
- }
1212
- static factoryFromApiResponse(responseIterator) {
1213
- return new _CairoFelt252(getNext(responseIterator));
1214
- }
1215
- };
1216
- function CairoFelt(it) {
1382
+ throw new Error("Invalid Tx version for hash calculation");
1383
+ }
1384
+ function isV3DeclareTx(args) {
1385
+ return [api_exports.ETransactionVersion.V3, api_exports.ETransactionVersion.F3].includes(args.version);
1386
+ }
1387
+ function calculateDeclareTransactionHash3(args) {
1388
+ if (isV3DeclareTx(args)) {
1389
+ return calculateDeclareTransactionHash(
1390
+ args.classHash,
1391
+ args.compiledClassHash,
1392
+ args.senderAddress,
1393
+ args.version,
1394
+ args.chainId,
1395
+ args.nonce,
1396
+ args.accountDeploymentData,
1397
+ args.nonceDataAvailabilityMode,
1398
+ args.feeDataAvailabilityMode,
1399
+ args.resourceBounds,
1400
+ args.tip,
1401
+ args.paymasterData
1402
+ );
1403
+ }
1404
+ throw new Error("Invalid Tx version for hash calculation");
1405
+ }
1406
+ function isV3DeployAccountTx(args) {
1407
+ return [api_exports.ETransactionVersion.V3, api_exports.ETransactionVersion.F3].includes(args.version);
1408
+ }
1409
+ function calculateDeployAccountTransactionHash3(args) {
1410
+ if (isV3DeployAccountTx(args)) {
1411
+ return calculateDeployAccountTransactionHash(
1412
+ args.contractAddress,
1413
+ args.classHash,
1414
+ args.compiledConstructorCalldata,
1415
+ args.salt,
1416
+ args.version,
1417
+ args.chainId,
1418
+ args.nonce,
1419
+ args.nonceDataAvailabilityMode,
1420
+ args.feeDataAvailabilityMode,
1421
+ args.resourceBounds,
1422
+ args.tip,
1423
+ args.paymasterData
1424
+ );
1425
+ }
1426
+ throw new Error("Invalid Tx version for hash calculation");
1427
+ }
1428
+
1429
+ // src/utils/calldata/byteArray.ts
1430
+ var byteArray_exports = {};
1431
+ __export(byteArray_exports, {
1432
+ byteArrayFromString: () => byteArrayFromString,
1433
+ stringFromByteArray: () => stringFromByteArray
1434
+ });
1435
+ function stringFromByteArray(myByteArray) {
1436
+ const pending_word = BigInt(myByteArray.pending_word) === 0n ? "" : decodeShortString(toHex(myByteArray.pending_word));
1437
+ return myByteArray.data.reduce((cumuledString, encodedString) => {
1438
+ const add = BigInt(encodedString) === 0n ? "" : decodeShortString(toHex(encodedString));
1439
+ return cumuledString + add;
1440
+ }, "") + pending_word;
1441
+ }
1442
+ function byteArrayFromString(targetString) {
1443
+ const shortStrings = splitLongString(targetString);
1444
+ const remainder = shortStrings[shortStrings.length - 1];
1445
+ const shortStringsEncoded = shortStrings.map(encodeShortString);
1446
+ const [pendingWord, pendingWordLength] = remainder === void 0 || remainder.length === 31 ? ["0x00", 0] : [shortStringsEncoded.pop(), remainder.length];
1447
+ return {
1448
+ data: shortStringsEncoded.length === 0 ? [] : shortStringsEncoded,
1449
+ pending_word: pendingWord,
1450
+ pending_word_len: pendingWordLength
1451
+ };
1452
+ }
1453
+
1454
+ // src/utils/calldata/cairo.ts
1455
+ var cairo_exports = {};
1456
+ __export(cairo_exports, {
1457
+ felt: () => felt,
1458
+ getAbiContractVersion: () => getAbiContractVersion,
1459
+ getArrayType: () => getArrayType,
1460
+ isCairo1Abi: () => isCairo1Abi,
1461
+ isCairo1Type: () => isCairo1Type,
1462
+ isLen: () => isLen,
1463
+ isTypeArray: () => isTypeArray,
1464
+ isTypeBool: () => isTypeBool,
1465
+ isTypeContractAddress: () => isTypeContractAddress,
1466
+ isTypeEnum: () => isTypeEnum,
1467
+ isTypeEthAddress: () => isTypeEthAddress,
1468
+ isTypeFelt: () => isTypeFelt,
1469
+ isTypeInt: () => isTypeInt,
1470
+ isTypeLiteral: () => isTypeLiteral,
1471
+ isTypeNamedTuple: () => isTypeNamedTuple,
1472
+ isTypeNonZero: () => isTypeNonZero,
1473
+ isTypeOption: () => isTypeOption,
1474
+ isTypeResult: () => isTypeResult,
1475
+ isTypeSecp256k1Point: () => isTypeSecp256k1Point,
1476
+ isTypeStruct: () => isTypeStruct,
1477
+ isTypeTuple: () => isTypeTuple,
1478
+ isTypeU96: () => isTypeU96,
1479
+ isTypeUint: () => isTypeUint,
1480
+ isTypeUint256: () => isTypeUint256,
1481
+ tuple: () => tuple,
1482
+ uint256: () => uint256,
1483
+ uint512: () => uint512
1484
+ });
1485
+
1486
+ // src/utils/helpers.ts
1487
+ function addCompiledFlag(compiled) {
1488
+ Object.defineProperty(compiled, "__compiled__", {
1489
+ enumerable: false,
1490
+ writable: false,
1491
+ value: true
1492
+ });
1493
+ return compiled;
1494
+ }
1495
+
1496
+ // src/utils/cairoDataTypes/felt.ts
1497
+ var CairoFelt252 = class _CairoFelt252 {
1498
+ /**
1499
+ * byte representation of the felt252
1500
+ */
1501
+ data;
1502
+ static abiSelector = "core::felt252";
1503
+ constructor(data) {
1504
+ _CairoFelt252.validate(data);
1505
+ const processedData = _CairoFelt252.__processData(data);
1506
+ this.data = processedData.subarray(processedData.findIndex((x) => x > 0));
1507
+ }
1508
+ static __processData(data) {
1509
+ if (isString(data)) {
1510
+ return stringToUint8Array(data);
1511
+ }
1512
+ if (isBigInt(data)) {
1513
+ return bigIntToUint8Array(data);
1514
+ }
1515
+ if (Number.isInteger(data)) {
1516
+ return bigIntToUint8Array(BigInt(data));
1517
+ }
1518
+ if (isBoolean(data)) {
1519
+ return bigIntToUint8Array(BigInt(data ? 1 : 0));
1520
+ }
1521
+ throw new Error(`${data} can't be computed by felt()`);
1522
+ }
1523
+ toBigInt() {
1524
+ return uint8ArrayToBigInt(this.data);
1525
+ }
1526
+ decodeUtf8() {
1527
+ return new TextDecoder().decode(this.data);
1528
+ }
1529
+ toHexString() {
1530
+ return addHexPrefix(this.toBigInt().toString(16));
1531
+ }
1532
+ toApiRequest() {
1533
+ return addCompiledFlag([BigInt(this.toHexString()).toString()]);
1534
+ }
1535
+ static assertRange(val) {
1536
+ assert(val >= 0n && val < PRIME, `Value ${val} is out of felt252 range [0, ${PRIME})`);
1537
+ }
1538
+ static validate(data) {
1539
+ assert(data !== null, "null value is not allowed for felt252");
1540
+ assert(data !== void 0, "undefined value is not allowed for felt252");
1541
+ assert(
1542
+ isString(data) || isNumber(data) || isBigInt(data) || isBoolean(data),
1543
+ `Unsupported data type '${typeof data}' for felt252. Expected string, number, bigint, or boolean`
1544
+ );
1545
+ const value = _CairoFelt252.__processData(data);
1546
+ const bn = uint8ArrayToBigInt(value);
1547
+ _CairoFelt252.assertRange(bn);
1548
+ }
1549
+ static is(data) {
1550
+ try {
1551
+ _CairoFelt252.validate(data);
1552
+ return true;
1553
+ } catch {
1554
+ return false;
1555
+ }
1556
+ }
1557
+ static isAbiType(abiType) {
1558
+ return abiType === _CairoFelt252.abiSelector;
1559
+ }
1560
+ static factoryFromApiResponse(responseIterator) {
1561
+ return new _CairoFelt252(getNext(responseIterator));
1562
+ }
1563
+ };
1564
+ function CairoFelt(it) {
1217
1565
  if (isBigInt(it) || Number.isInteger(it)) {
1218
1566
  const val = BigInt(it);
1219
1567
  CairoFelt252.assertRange(val);
@@ -1855,134 +2203,14 @@ var CairoBytes31 = class _CairoBytes31 {
1855
2203
  }
1856
2204
  };
1857
2205
 
1858
- // src/utils/ec.ts
1859
- var ec_exports = {};
1860
- __export(ec_exports, {
1861
- starkCurve: () => starkCurve,
1862
- weierstrass: () => weierstrass
1863
- });
1864
- import * as starkCurve from "@scure/starknet";
1865
- import * as weierstrass from "@noble/curves/abstract/weierstrass";
1866
-
1867
2206
  // src/utils/hash/pedersenCore.ts
1868
2207
  function computePedersenHash(a, b) {
1869
2208
  return starkCurve.pedersen(BigInt(a), BigInt(b));
1870
2209
  }
1871
- function computeHashOnElements(data) {
2210
+ function computeHashOnElements2(data) {
1872
2211
  return [...data, data.length].reduce((x, y) => starkCurve.pedersen(BigInt(x), BigInt(y)), 0).toString();
1873
2212
  }
1874
- var computePedersenHashOnElements = computeHashOnElements;
1875
-
1876
- // src/utils/errors/rpc.ts
1877
- var errorCodes = {
1878
- FAILED_TO_RECEIVE_TXN: 1,
1879
- NO_TRACE_AVAILABLE: 10,
1880
- CONTRACT_NOT_FOUND: 20,
1881
- ENTRYPOINT_NOT_FOUND: 21,
1882
- BLOCK_NOT_FOUND: 24,
1883
- INVALID_TXN_INDEX: 27,
1884
- CLASS_HASH_NOT_FOUND: 28,
1885
- TXN_HASH_NOT_FOUND: 29,
1886
- PAGE_SIZE_TOO_BIG: 31,
1887
- NO_BLOCKS: 32,
1888
- INVALID_CONTINUATION_TOKEN: 33,
1889
- TOO_MANY_KEYS_IN_FILTER: 34,
1890
- CONTRACT_ERROR: 40,
1891
- TRANSACTION_EXECUTION_ERROR: 41,
1892
- STORAGE_PROOF_NOT_SUPPORTED: 42,
1893
- CLASS_ALREADY_DECLARED: 51,
1894
- INVALID_TRANSACTION_NONCE: 52,
1895
- INSUFFICIENT_RESOURCES_FOR_VALIDATE: 53,
1896
- INSUFFICIENT_ACCOUNT_BALANCE: 54,
1897
- VALIDATION_FAILURE: 55,
1898
- COMPILATION_FAILED: 56,
1899
- CONTRACT_CLASS_SIZE_IS_TOO_LARGE: 57,
1900
- NON_ACCOUNT: 58,
1901
- DUPLICATE_TX: 59,
1902
- COMPILED_CLASS_HASH_MISMATCH: 60,
1903
- UNSUPPORTED_TX_VERSION: 61,
1904
- UNSUPPORTED_CONTRACT_CLASS_VERSION: 62,
1905
- UNEXPECTED_ERROR: 63,
1906
- REPLACEMENT_TRANSACTION_UNDERPRICED: 64,
1907
- FEE_BELOW_MINIMUM: 65,
1908
- INVALID_SUBSCRIPTION_ID: 66,
1909
- TOO_MANY_ADDRESSES_IN_FILTER: 67,
1910
- TOO_MANY_BLOCKS_BACK: 68,
1911
- COMPILATION_ERROR: 100,
1912
- //
1913
- INVALID_ADDRESS: 150,
1914
- TOKEN_NOT_SUPPORTED: 151,
1915
- INVALID_SIGNATURE: 153,
1916
- MAX_AMOUNT_TOO_LOW: 154,
1917
- CLASS_HASH_NOT_SUPPORTED: 155,
1918
- PAYMASTER_TRANSACTION_EXECUTION_ERROR: 156,
1919
- INVALID_TIME_BOUNDS: 157,
1920
- INVALID_DEPLOYMENT_DATA: 158,
1921
- INVALID_CLASS_HASH: 159,
1922
- INVALID_ID: 160,
1923
- UNKNOWN_ERROR: 163
1924
- };
1925
- var rpc_default = errorCodes;
1926
-
1927
- // src/utils/errors/index.ts
1928
- function fixStack(target, fn = target.constructor) {
1929
- const { captureStackTrace } = Error;
1930
- captureStackTrace && captureStackTrace(target, fn);
1931
- }
1932
- function fixProto(target, prototype) {
1933
- const { setPrototypeOf } = Object;
1934
- setPrototypeOf ? setPrototypeOf(target, prototype) : target.__proto__ = prototype;
1935
- }
1936
- var CustomError = class extends Error {
1937
- name;
1938
- constructor(message) {
1939
- super(message);
1940
- Object.defineProperty(this, "name", {
1941
- value: new.target.name,
1942
- enumerable: false,
1943
- configurable: true
1944
- });
1945
- fixProto(this, new.target.prototype);
1946
- fixStack(this);
1947
- }
1948
- };
1949
- var LibraryError = class extends CustomError {
1950
- };
1951
- var RpcError = class extends LibraryError {
1952
- constructor(baseError, method, params) {
1953
- super(`RPC: ${method} with params ${stringify2(params, null, 2)}
1954
-
1955
- ${baseError.code}: ${baseError.message}: ${stringify2(baseError.data)}`);
1956
- this.baseError = baseError;
1957
- this.request = { method, params };
1958
- }
1959
- request;
1960
- get code() {
1961
- return this.baseError.code;
1962
- }
1963
- /**
1964
- * Verifies the underlying RPC error, also serves as a type guard for the _baseError_ property
1965
- * @example
1966
- * ```typescript
1967
- * SomeError.isType('UNEXPECTED_ERROR');
1968
- * ```
1969
- */
1970
- isType(typeName) {
1971
- return rpc_default[typeName] === this.code;
1972
- }
1973
- };
1974
- var TimeoutError = class extends LibraryError {
1975
- constructor(message) {
1976
- super(message);
1977
- this.name = "TimeoutError";
1978
- }
1979
- };
1980
- var WebSocketNotConnectedError = class extends LibraryError {
1981
- constructor(message) {
1982
- super(message);
1983
- this.name = "WebSocketNotConnectedError";
1984
- }
1985
- };
2213
+ var computePedersenHashOnElements = computeHashOnElements2;
1986
2214
 
1987
2215
  // src/utils/connect/buffer.ts
1988
2216
  var buffer_default = config.get("buffer") || typeof Buffer !== "undefined" && Buffer || typeof globalThis !== "undefined" && globalThis.Buffer || typeof window !== "undefined" && window.Buffer || typeof global !== "undefined" && global.Buffer || class {
@@ -2190,7 +2418,7 @@ var CairoByteArray = class _CairoByteArray {
2190
2418
  ...this.pending_word.toApiRequest(),
2191
2419
  ...this.pending_word_len.toApiRequest()
2192
2420
  ];
2193
- return computeHashOnElements(serialized);
2421
+ return computeHashOnElements2(serialized);
2194
2422
  }
2195
2423
  /**
2196
2424
  * returns an array of all the data chunks and the pending word
@@ -4749,381 +4977,89 @@ var CallData = class _CallData {
4749
4977
  }
4750
4978
  };
4751
4979
 
4752
- // src/utils/hash/index.ts
4753
- var hash_exports = {};
4754
- __export(hash_exports, {
4755
- COMPILED_CLASS_VERSION: () => COMPILED_CLASS_VERSION,
4756
- blake2sHashMany: () => blake2sHashMany,
4757
- calculateContractAddressFromHash: () => calculateContractAddressFromHash,
4758
- calculateDeclareTransactionHash: () => calculateDeclareTransactionHash3,
4759
- calculateDeployAccountTransactionHash: () => calculateDeployAccountTransactionHash3,
4760
- calculateInvokeTransactionHash: () => calculateInvokeTransactionHash2,
4761
- calculateL2MessageTxHash: () => calculateL2MessageTxHash,
4762
- computeCompiledClassHash: () => computeCompiledClassHash,
4763
- computeCompiledClassHashBlake: () => computeCompiledClassHashBlake,
4764
- computeCompiledClassHashPoseidon: () => computeCompiledClassHashPoseidon,
4765
- computeContractClassHash: () => computeContractClassHash,
4766
- computeHashOnElements: () => computeHashOnElements,
4767
- computeHintedClassHash: () => computeHintedClassHash,
4768
- computeLegacyContractClassHash: () => computeLegacyContractClassHash,
4769
- computePedersenHash: () => computePedersenHash,
4770
- computePedersenHashOnElements: () => computePedersenHashOnElements,
4771
- computePoseidonHash: () => computePoseidonHash,
4772
- computePoseidonHashOnElements: () => computePoseidonHashOnElements,
4773
- computeSierraContractClassHash: () => computeSierraContractClassHash,
4774
- encodeBuiltins: () => encodeBuiltins,
4775
- flattenEntryPointData: () => flattenEntryPointData,
4776
- formatSpaces: () => formatSpaces,
4777
- getL1MessageHash: () => getL1MessageHash,
4778
- getL2MessageHash: () => getL2MessageHash,
4779
- getSelector: () => getSelector,
4780
- getSelectorFromName: () => getSelectorFromName,
4781
- hashByteCodeSegments: () => hashByteCodeSegments,
4782
- hashByteCodeSegmentsBlake: () => hashByteCodeSegmentsBlake,
4783
- keccakBn: () => keccakBn,
4784
- nullSkipReplacer: () => nullSkipReplacer,
4785
- poseidon: () => poseidon,
4786
- solidityUint256PackedKeccak256: () => solidityUint256PackedKeccak256,
4787
- starknetKeccak: () => starknetKeccak
4788
- });
4789
- import * as poseidon from "@noble/curves/abstract/poseidon";
4790
-
4791
- // src/utils/hash/transactionHash/v3.ts
4792
- var v3_exports = {};
4793
- __export(v3_exports, {
4794
- calculateDeclareTransactionHash: () => calculateDeclareTransactionHash,
4795
- calculateDeployAccountTransactionHash: () => calculateDeployAccountTransactionHash,
4796
- calculateInvokeTransactionHash: () => calculateInvokeTransactionHash,
4797
- calculateTransactionHashCommon: () => calculateTransactionHashCommon,
4798
- encodeDataResourceBoundsL1: () => encodeDataResourceBoundsL1,
4799
- encodeResourceBoundsL1: () => encodeResourceBoundsL1,
4800
- encodeResourceBoundsL2: () => encodeResourceBoundsL2,
4801
- hashDAMode: () => hashDAMode,
4802
- hashFeeFieldV3B3: () => hashFeeFieldV3B3
4803
- });
4804
- import { poseidonHashMany } from "@scure/starknet";
4805
- var AToBI = (array) => array.map((it) => BigInt(it));
4806
- var DATA_AVAILABILITY_MODE_BITS = 32n;
4807
- var MAX_AMOUNT_BITS = 64n;
4808
- var MAX_PRICE_PER_UNIT_BITS = 128n;
4809
- var RESOURCE_VALUE_OFFSET = MAX_AMOUNT_BITS + MAX_PRICE_PER_UNIT_BITS;
4810
- var L1_GAS_NAME = BigInt(encodeShortString("L1_GAS"));
4811
- var L2_GAS_NAME = BigInt(encodeShortString("L2_GAS"));
4812
- var L1_DATA_GAS_NAME = BigInt(encodeShortString("L1_DATA"));
4813
- function hashDAMode(nonceDAMode, feeDAMode) {
4814
- return (BigInt(nonceDAMode) << DATA_AVAILABILITY_MODE_BITS) + BigInt(feeDAMode);
4815
- }
4816
- function encodeResourceBoundsL1(bounds) {
4817
- return (L1_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.l1_gas.max_amount << MAX_PRICE_PER_UNIT_BITS) + bounds.l1_gas.max_price_per_unit;
4980
+ // src/utils/hash/classHash/util.ts
4981
+ var COMPILED_CLASS_VERSION = "COMPILED_CLASS_V1";
4982
+ function formatSpaces(json2) {
4983
+ let insideQuotes = false;
4984
+ const newString = [];
4985
+ for (const char of json2) {
4986
+ if (char === '"' && (newString.length > 0 && newString.slice(-1)[0] === "\\") === false) {
4987
+ insideQuotes = !insideQuotes;
4988
+ }
4989
+ if (insideQuotes) {
4990
+ newString.push(char);
4991
+ } else {
4992
+ newString.push(char === ":" ? ": " : char === "," ? ", " : char);
4993
+ }
4994
+ }
4995
+ return newString.join("");
4818
4996
  }
4819
- function encodeResourceBoundsL2(bounds) {
4820
- return (L2_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.l2_gas.max_amount << MAX_PRICE_PER_UNIT_BITS) + bounds.l2_gas.max_price_per_unit;
4997
+ function nullSkipReplacer(key, value) {
4998
+ if (key === "attributes" || key === "accessible_scopes") {
4999
+ return Array.isArray(value) && value.length === 0 ? void 0 : value;
5000
+ }
5001
+ if (key === "debug_info") {
5002
+ return null;
5003
+ }
5004
+ return value === null ? void 0 : value;
4821
5005
  }
4822
- function encodeDataResourceBoundsL1(bounds) {
4823
- return (L1_DATA_GAS_NAME << RESOURCE_VALUE_OFFSET) + (bounds.l1_data_gas.max_amount << MAX_PRICE_PER_UNIT_BITS) + bounds.l1_data_gas.max_price_per_unit;
5006
+ function encodeBuiltins(builtins) {
5007
+ return builtins.map((it) => BigInt(encodeShortString(it)));
4824
5008
  }
4825
- function hashFeeFieldV3B3(tip, bounds) {
4826
- const L1Bound = encodeResourceBoundsL1(bounds);
4827
- const L2Bound = encodeResourceBoundsL2(bounds);
4828
- const L1Data = encodeDataResourceBoundsL1(bounds);
4829
- return poseidonHashMany([BigInt(tip), L1Bound, L2Bound, L1Data]);
5009
+ function flattenEntryPointData(data, encodedBuiltinsArray) {
5010
+ return data.flatMap((it, index) => [
5011
+ BigInt(it.selector),
5012
+ BigInt(it.offset),
5013
+ ...encodedBuiltinsArray[index]
5014
+ ]);
4830
5015
  }
4831
- function calculateTransactionHashCommon(txHashPrefix, version, senderAddress, chainId, nonce, tip, paymasterData, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, additionalData = []) {
4832
- const feeFieldHash = hashFeeFieldV3B3(tip, resourceBounds);
4833
- const dAModeHash = hashDAMode(nonceDataAvailabilityMode, feeDataAvailabilityMode);
4834
- const dataToHash = AToBI([
4835
- txHashPrefix,
4836
- version,
4837
- senderAddress,
4838
- feeFieldHash,
4839
- poseidonHashMany(AToBI(paymasterData)),
4840
- chainId,
4841
- nonce,
4842
- dAModeHash,
4843
- ...AToBI(additionalData)
5016
+
5017
+ // src/utils/hash/classHash/pedersen.ts
5018
+ function calculateContractAddressFromHash(salt, classHash, constructorCalldata, deployerAddress) {
5019
+ const compiledCalldata = CallData.compile(constructorCalldata);
5020
+ const constructorCalldataHash = computeHashOnElements2(compiledCalldata);
5021
+ const CONTRACT_ADDRESS_PREFIX = felt("0x535441524b4e45545f434f4e54524143545f41444452455353");
5022
+ const hash = computeHashOnElements2([
5023
+ CONTRACT_ADDRESS_PREFIX,
5024
+ deployerAddress,
5025
+ salt,
5026
+ classHash,
5027
+ constructorCalldataHash
4844
5028
  ]);
4845
- return toHex(poseidonHashMany(dataToHash));
5029
+ return toHex(BigInt(hash) % ADDR_BOUND);
4846
5030
  }
4847
- function calculateDeployAccountTransactionHash(contractAddress, classHash, compiledConstructorCalldata, salt, version, chainId, nonce, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, tip, paymasterData) {
4848
- return calculateTransactionHashCommon(
4849
- _TransactionHashPrefix.DEPLOY_ACCOUNT,
4850
- version,
4851
- contractAddress,
4852
- chainId,
4853
- nonce,
4854
- tip,
4855
- paymasterData,
4856
- nonceDataAvailabilityMode,
4857
- feeDataAvailabilityMode,
4858
- resourceBounds,
4859
- [poseidonHashMany(AToBI(compiledConstructorCalldata)), classHash, salt]
4860
- );
5031
+ function computeHintedClassHash(compiledContract) {
5032
+ const { abi, program } = compiledContract;
5033
+ const contractClass = { abi, program };
5034
+ const serializedJson = formatSpaces(stringify2(contractClass, nullSkipReplacer));
5035
+ return addHexPrefix(starkCurve.keccak(utf8ToArray(serializedJson)).toString(16));
4861
5036
  }
4862
- function calculateDeclareTransactionHash(classHash, compiledClassHash, senderAddress, version, chainId, nonce, accountDeploymentData, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, tip, paymasterData) {
4863
- return calculateTransactionHashCommon(
4864
- _TransactionHashPrefix.DECLARE,
4865
- version,
4866
- senderAddress,
4867
- chainId,
4868
- nonce,
4869
- tip,
4870
- AToBI(paymasterData),
4871
- nonceDataAvailabilityMode,
4872
- feeDataAvailabilityMode,
4873
- resourceBounds,
4874
- [poseidonHashMany(AToBI(accountDeploymentData)), classHash, compiledClassHash]
5037
+ function computeLegacyContractClassHash(contract) {
5038
+ const compiledContract = isString(contract) ? parse2(contract) : contract;
5039
+ const apiVersion = toHex(API_VERSION);
5040
+ const externalEntryPointsHash = computeHashOnElements2(
5041
+ compiledContract.entry_points_by_type.EXTERNAL.flatMap((e) => [e.selector, e.offset])
4875
5042
  );
4876
- }
4877
- function calculateInvokeTransactionHash(senderAddress, version, compiledCalldata, chainId, nonce, accountDeploymentData, nonceDataAvailabilityMode, feeDataAvailabilityMode, resourceBounds, tip, paymasterData, proofFacts) {
4878
- const proofFactsAdditionalData = proofFacts?.length ? [poseidonHashMany(AToBI(proofFacts))] : [];
4879
- return calculateTransactionHashCommon(
4880
- _TransactionHashPrefix.INVOKE,
4881
- version,
4882
- senderAddress,
4883
- chainId,
4884
- nonce,
4885
- tip,
4886
- paymasterData,
4887
- nonceDataAvailabilityMode,
4888
- feeDataAvailabilityMode,
4889
- resourceBounds,
4890
- [
4891
- poseidonHashMany(AToBI(accountDeploymentData)),
4892
- poseidonHashMany(AToBI(compiledCalldata)),
4893
- ...proofFactsAdditionalData
4894
- ]
5043
+ const l1HandlerEntryPointsHash = computeHashOnElements2(
5044
+ compiledContract.entry_points_by_type.L1_HANDLER.flatMap((e) => [e.selector, e.offset])
5045
+ );
5046
+ const constructorEntryPointHash = computeHashOnElements2(
5047
+ compiledContract.entry_points_by_type.CONSTRUCTOR.flatMap((e) => [e.selector, e.offset])
4895
5048
  );
4896
- }
4897
-
4898
- // src/utils/hash/transactionHash/v2.ts
4899
- var v2_exports = {};
4900
- __export(v2_exports, {
4901
- calculateDeclareTransactionHash: () => calculateDeclareTransactionHash2,
4902
- calculateDeployAccountTransactionHash: () => calculateDeployAccountTransactionHash2,
4903
- calculateL2MessageTxHash: () => calculateL2MessageTxHash,
4904
- calculateTransactionHash: () => calculateTransactionHash,
4905
- calculateTransactionHashCommon: () => calculateTransactionHashCommon2,
4906
- computeHashOnElements: () => computeHashOnElements2
4907
- });
4908
- function computeHashOnElements2(data) {
4909
- return [...data, data.length].reduce((x, y) => starkCurve.pedersen(toBigInt(x), toBigInt(y)), 0).toString();
4910
- }
4911
- function calculateTransactionHashCommon2(txHashPrefix, version, contractAddress, entryPointSelector, calldata, maxFee, chainId, additionalData = []) {
4912
- const calldataHash = computeHashOnElements2(calldata);
4913
- const dataToHash = [
4914
- txHashPrefix,
4915
- version,
4916
- contractAddress,
4917
- entryPointSelector,
4918
- calldataHash,
4919
- maxFee,
4920
- chainId,
4921
- ...additionalData
4922
- ];
4923
- return computeHashOnElements2(dataToHash);
4924
- }
4925
- function calculateDeclareTransactionHash2(classHash, senderAddress, version, maxFee, chainId, nonce, compiledClassHash) {
4926
- return calculateTransactionHashCommon2(
4927
- _TransactionHashPrefix.DECLARE,
4928
- version,
4929
- senderAddress,
4930
- 0,
4931
- [classHash],
4932
- maxFee,
4933
- chainId,
4934
- [nonce, ...compiledClassHash ? [compiledClassHash] : []]
4935
- );
4936
- }
4937
- function calculateDeployAccountTransactionHash2(contractAddress, classHash, constructorCalldata, salt, version, maxFee, chainId, nonce) {
4938
- const calldata = [classHash, salt, ...constructorCalldata];
4939
- return calculateTransactionHashCommon2(
4940
- _TransactionHashPrefix.DEPLOY_ACCOUNT,
4941
- version,
4942
- contractAddress,
4943
- 0,
4944
- calldata,
4945
- maxFee,
4946
- chainId,
4947
- [nonce]
4948
- );
4949
- }
4950
- function calculateTransactionHash(contractAddress, version, calldata, maxFee, chainId, nonce) {
4951
- return calculateTransactionHashCommon2(
4952
- _TransactionHashPrefix.INVOKE,
4953
- version,
4954
- contractAddress,
4955
- 0,
4956
- calldata,
4957
- maxFee,
4958
- chainId,
4959
- [nonce]
4960
- );
4961
- }
4962
- function calculateL2MessageTxHash(l1FromAddress, l2ToAddress, l2Selector, l2Calldata, l2ChainId, l1Nonce) {
4963
- const payload = [l1FromAddress, ...l2Calldata];
4964
- return calculateTransactionHashCommon2(
4965
- _TransactionHashPrefix.L1_HANDLER,
4966
- 0,
4967
- l2ToAddress,
4968
- getSelector(l2Selector),
4969
- payload,
4970
- 0,
4971
- l2ChainId,
4972
- [l1Nonce]
4973
- );
4974
- }
4975
-
4976
- // src/utils/hash/transactionHash/index.ts
4977
- function isV3InvokeTx(args) {
4978
- return [api_exports.ETransactionVersion.V3, api_exports.ETransactionVersion.F3].includes(args.version);
4979
- }
4980
- function calculateInvokeTransactionHash2(args) {
4981
- if (isV3InvokeTx(args)) {
4982
- return calculateInvokeTransactionHash(
4983
- args.senderAddress,
4984
- args.version,
4985
- args.compiledCalldata,
4986
- args.chainId,
4987
- args.nonce,
4988
- args.accountDeploymentData,
4989
- args.nonceDataAvailabilityMode,
4990
- args.feeDataAvailabilityMode,
4991
- args.resourceBounds,
4992
- args.tip,
4993
- args.paymasterData,
4994
- args.proofFacts
4995
- );
4996
- }
4997
- throw new Error("Invalid Tx version for hash calculation");
4998
- }
4999
- function isV3DeclareTx(args) {
5000
- return [api_exports.ETransactionVersion.V3, api_exports.ETransactionVersion.F3].includes(args.version);
5001
- }
5002
- function calculateDeclareTransactionHash3(args) {
5003
- if (isV3DeclareTx(args)) {
5004
- return calculateDeclareTransactionHash(
5005
- args.classHash,
5006
- args.compiledClassHash,
5007
- args.senderAddress,
5008
- args.version,
5009
- args.chainId,
5010
- args.nonce,
5011
- args.accountDeploymentData,
5012
- args.nonceDataAvailabilityMode,
5013
- args.feeDataAvailabilityMode,
5014
- args.resourceBounds,
5015
- args.tip,
5016
- args.paymasterData
5017
- );
5018
- }
5019
- throw new Error("Invalid Tx version for hash calculation");
5020
- }
5021
- function isV3DeployAccountTx(args) {
5022
- return [api_exports.ETransactionVersion.V3, api_exports.ETransactionVersion.F3].includes(args.version);
5023
- }
5024
- function calculateDeployAccountTransactionHash3(args) {
5025
- if (isV3DeployAccountTx(args)) {
5026
- return calculateDeployAccountTransactionHash(
5027
- args.contractAddress,
5028
- args.classHash,
5029
- args.compiledConstructorCalldata,
5030
- args.salt,
5031
- args.version,
5032
- args.chainId,
5033
- args.nonce,
5034
- args.nonceDataAvailabilityMode,
5035
- args.feeDataAvailabilityMode,
5036
- args.resourceBounds,
5037
- args.tip,
5038
- args.paymasterData
5039
- );
5040
- }
5041
- throw new Error("Invalid Tx version for hash calculation");
5042
- }
5043
-
5044
- // src/utils/hash/classHash/util.ts
5045
- var COMPILED_CLASS_VERSION = "COMPILED_CLASS_V1";
5046
- function formatSpaces(json2) {
5047
- let insideQuotes = false;
5048
- const newString = [];
5049
- for (const char of json2) {
5050
- if (char === '"' && (newString.length > 0 && newString.slice(-1)[0] === "\\") === false) {
5051
- insideQuotes = !insideQuotes;
5052
- }
5053
- if (insideQuotes) {
5054
- newString.push(char);
5055
- } else {
5056
- newString.push(char === ":" ? ": " : char === "," ? ", " : char);
5057
- }
5058
- }
5059
- return newString.join("");
5060
- }
5061
- function nullSkipReplacer(key, value) {
5062
- if (key === "attributes" || key === "accessible_scopes") {
5063
- return Array.isArray(value) && value.length === 0 ? void 0 : value;
5064
- }
5065
- if (key === "debug_info") {
5066
- return null;
5067
- }
5068
- return value === null ? void 0 : value;
5069
- }
5070
- function encodeBuiltins(builtins) {
5071
- return builtins.map((it) => BigInt(encodeShortString(it)));
5072
- }
5073
- function flattenEntryPointData(data, encodedBuiltinsArray) {
5074
- return data.flatMap((it, index) => [
5075
- BigInt(it.selector),
5076
- BigInt(it.offset),
5077
- ...encodedBuiltinsArray[index]
5078
- ]);
5079
- }
5080
-
5081
- // src/utils/hash/classHash/pedersen.ts
5082
- function calculateContractAddressFromHash(salt, classHash, constructorCalldata, deployerAddress) {
5083
- const compiledCalldata = CallData.compile(constructorCalldata);
5084
- const constructorCalldataHash = computeHashOnElements(compiledCalldata);
5085
- const CONTRACT_ADDRESS_PREFIX = felt("0x535441524b4e45545f434f4e54524143545f41444452455353");
5086
- const hash = computeHashOnElements([
5087
- CONTRACT_ADDRESS_PREFIX,
5088
- deployerAddress,
5089
- salt,
5090
- classHash,
5091
- constructorCalldataHash
5092
- ]);
5093
- return toHex(BigInt(hash) % ADDR_BOUND);
5094
- }
5095
- function computeHintedClassHash(compiledContract) {
5096
- const { abi, program } = compiledContract;
5097
- const contractClass = { abi, program };
5098
- const serializedJson = formatSpaces(stringify2(contractClass, nullSkipReplacer));
5099
- return addHexPrefix(starkCurve.keccak(utf8ToArray(serializedJson)).toString(16));
5100
- }
5101
- function computeLegacyContractClassHash(contract) {
5102
- const compiledContract = isString(contract) ? parse2(contract) : contract;
5103
- const apiVersion = toHex(API_VERSION);
5104
- const externalEntryPointsHash = computeHashOnElements(
5105
- compiledContract.entry_points_by_type.EXTERNAL.flatMap((e) => [e.selector, e.offset])
5106
- );
5107
- const l1HandlerEntryPointsHash = computeHashOnElements(
5108
- compiledContract.entry_points_by_type.L1_HANDLER.flatMap((e) => [e.selector, e.offset])
5109
- );
5110
- const constructorEntryPointHash = computeHashOnElements(
5111
- compiledContract.entry_points_by_type.CONSTRUCTOR.flatMap((e) => [e.selector, e.offset])
5112
- );
5113
- const builtinsHash = computeHashOnElements(
5114
- compiledContract.program.builtins.map((s) => encodeShortString(s))
5115
- );
5116
- const hintedClassHash = computeHintedClassHash(compiledContract);
5117
- const dataHash = computeHashOnElements(compiledContract.program.data);
5118
- return computeHashOnElements([
5119
- apiVersion,
5120
- externalEntryPointsHash,
5121
- l1HandlerEntryPointsHash,
5122
- constructorEntryPointHash,
5123
- builtinsHash,
5124
- hintedClassHash,
5125
- dataHash
5126
- ]);
5049
+ const builtinsHash = computeHashOnElements2(
5050
+ compiledContract.program.builtins.map((s) => encodeShortString(s))
5051
+ );
5052
+ const hintedClassHash = computeHintedClassHash(compiledContract);
5053
+ const dataHash = computeHashOnElements2(compiledContract.program.data);
5054
+ return computeHashOnElements2([
5055
+ apiVersion,
5056
+ externalEntryPointsHash,
5057
+ l1HandlerEntryPointsHash,
5058
+ constructorEntryPointHash,
5059
+ builtinsHash,
5060
+ hintedClassHash,
5061
+ dataHash
5062
+ ]);
5127
5063
  }
5128
5064
 
5129
5065
  // src/utils/hash/classHash/poseidon.ts
@@ -5200,7 +5136,7 @@ function computeSierraContractClassHash(sierra) {
5200
5136
  }
5201
5137
 
5202
5138
  // src/utils/connect/blake.ts
5203
- import { blake2s } from "@noble/hashes/blake2s";
5139
+ import { blake2s } from "@noble/hashes/blake2.js";
5204
5140
  function blakeHash(uint8Array) {
5205
5141
  return config.get("blake")?.(uint8Array) || blake2s(uint8Array, { dkLen: 32 });
5206
5142
  }
@@ -5380,6 +5316,8 @@ __export(stark_exports, {
5380
5316
  getSharedSecret: () => getSharedSecret,
5381
5317
  intDAM: () => intDAM,
5382
5318
  randomAddress: () => randomAddress,
5319
+ randomFelt: () => randomFelt,
5320
+ randomStarkPrivateKey: () => randomStarkPrivateKey,
5383
5321
  resourceBoundsToBigInt: () => resourceBoundsToBigInt,
5384
5322
  resourceBoundsToEstimateFeeResponse: () => resourceBoundsToEstimateFeeResponse,
5385
5323
  resourceBoundsToHexString: () => resourceBoundsToHexString,
@@ -5392,7 +5330,8 @@ __export(stark_exports, {
5392
5330
  v3Details: () => v3Details,
5393
5331
  zeroResourceBounds: () => zeroResourceBounds
5394
5332
  });
5395
- import { getPublicKey, getStarkKey, utils } from "@scure/starknet";
5333
+ import { randomBytes } from "@noble/hashes/utils.js";
5334
+ import { getPublicKey, utils } from "@scure/starknet";
5396
5335
  async function compressProgram(jsonProgram) {
5397
5336
  const stringified = isString(jsonProgram) ? jsonProgram : stringify2(jsonProgram);
5398
5337
  const stream = new CompressionStream("gzip");
@@ -5412,9 +5351,14 @@ async function decompressProgram(base642) {
5412
5351
  const decompressed = await new Response(stream.readable).text();
5413
5352
  return parse2(decompressed);
5414
5353
  }
5354
+ function randomFelt() {
5355
+ return toHex(uint8ArrayToBigInt(randomBytes(48)) % PRIME);
5356
+ }
5357
+ function randomStarkPrivateKey() {
5358
+ return addHexPrefix(buf2hex(utils.randomPrivateKey()));
5359
+ }
5415
5360
  function randomAddress() {
5416
- const randomKeyPair = utils.randomPrivateKey();
5417
- return getStarkKey(randomKeyPair);
5361
+ return randomFelt();
5418
5362
  }
5419
5363
  function encodeProof(proof) {
5420
5364
  return btoaUniversal(new Uint32Array(proof).buffer);
@@ -5433,7 +5377,7 @@ function formatSignature(sig) {
5433
5377
  const { r, s } = sig;
5434
5378
  return [toHex(r), toHex(s)];
5435
5379
  } catch (e) {
5436
- throw new Error("Signature need to be weierstrass.SignatureType or an array for custom");
5380
+ throw new Error("Signature need to be weierstrass.ECDSASignature or an array for custom");
5437
5381
  }
5438
5382
  }
5439
5383
  function signatureToDecimalArray(sig) {
@@ -5570,413 +5514,1564 @@ function resourceBoundsToHexString(resourceBoundsBN) {
5570
5514
  if (isBigInt(obj)) {
5571
5515
  return toHex(obj);
5572
5516
  }
5573
- if (isObject(obj)) {
5574
- const result = {};
5575
- Object.keys(obj).forEach((key) => {
5576
- result[key] = convertBigIntToHex(obj[key]);
5577
- });
5578
- return result;
5517
+ if (isObject(obj)) {
5518
+ const result = {};
5519
+ Object.keys(obj).forEach((key) => {
5520
+ result[key] = convertBigIntToHex(obj[key]);
5521
+ });
5522
+ return result;
5523
+ }
5524
+ return obj;
5525
+ };
5526
+ return convertBigIntToHex(resourceBoundsBN);
5527
+ }
5528
+ function resourceBoundsToBigInt(resourceBounds) {
5529
+ const convertStringToBigInt = (obj) => {
5530
+ if (isString(obj)) {
5531
+ return BigInt(obj);
5532
+ }
5533
+ if (isObject(obj)) {
5534
+ const result = {};
5535
+ Object.keys(obj).forEach((key) => {
5536
+ result[key] = convertStringToBigInt(obj[key]);
5537
+ });
5538
+ return result;
5539
+ }
5540
+ return obj;
5541
+ };
5542
+ return convertStringToBigInt(resourceBounds);
5543
+ }
5544
+
5545
+ // src/utils/contractLoader.ts
5546
+ function isFileSystemAvailable() {
5547
+ try {
5548
+ return typeof __require !== "undefined" && typeof __require.resolve === "function";
5549
+ } catch {
5550
+ logger.info("isFileSystemAvailable: false");
5551
+ return false;
5552
+ }
5553
+ }
5554
+ function contractLoader(input, compiledClassHash) {
5555
+ if (typeof File !== "undefined" && (input instanceof File || Array.isArray(input))) {
5556
+ return loadFromFileAPI(input, compiledClassHash);
5557
+ }
5558
+ if (typeof input === "string") {
5559
+ if (!isFileSystemAvailable()) {
5560
+ throw new Error(
5561
+ 'contractLoader with string paths is only available in Node.js environments. In browsers, please use File objects from <input type="file"> or drag-and-drop. Example: await contractLoader(fileInput.files[0])'
5562
+ );
5563
+ }
5564
+ return loadFromFileSystem(input, compiledClassHash);
5565
+ }
5566
+ throw new Error(
5567
+ "Invalid input type. Expected string (Node.js path) or File/File[] (browser File API)"
5568
+ );
5569
+ }
5570
+ function loadFromFileSystem(contractPath, compiledClassHash) {
5571
+ const fs = __require("fs");
5572
+ const path = __require("path");
5573
+ const resolvedPath = path.resolve(contractPath);
5574
+ let dirPath;
5575
+ let specifiedSierraFile;
5576
+ let specifiedCasmFile;
5577
+ const stats = fs.statSync(resolvedPath);
5578
+ if (stats.isFile()) {
5579
+ dirPath = path.dirname(resolvedPath);
5580
+ const fileName = path.basename(resolvedPath);
5581
+ if (fileName.endsWith(".sierra.json") || fileName.endsWith(".json") && !fileName.endsWith(".casm")) {
5582
+ specifiedSierraFile = fileName;
5583
+ } else if (fileName.endsWith(".casm")) {
5584
+ specifiedCasmFile = fileName;
5585
+ } else {
5586
+ throw new Error(
5587
+ `Invalid file type. Expected .json, .sierra.json, or .casm file, got: ${fileName}`
5588
+ );
5589
+ }
5590
+ } else if (stats.isDirectory()) {
5591
+ dirPath = resolvedPath;
5592
+ } else {
5593
+ throw new Error(`Path is neither a file nor a directory: ${contractPath}`);
5594
+ }
5595
+ const files = fs.readdirSync(dirPath);
5596
+ let sierraFile;
5597
+ let casmFile;
5598
+ if (specifiedSierraFile) {
5599
+ sierraFile = specifiedSierraFile;
5600
+ const baseName = sierraFile.replace(/\.sierra\.json$/, "").replace(/\.json$/, "");
5601
+ casmFile = files.find((f) => f === `${baseName}.casm`);
5602
+ } else if (specifiedCasmFile) {
5603
+ casmFile = specifiedCasmFile;
5604
+ const baseName = casmFile.replace(/\.casm$/, "");
5605
+ sierraFile = files.find((f) => f === `${baseName}.sierra.json`) || files.find((f) => f === `${baseName}.json`);
5606
+ } else {
5607
+ const sierraFiles = files.filter(
5608
+ (f) => f.endsWith(".sierra.json") || f.endsWith(".json") && !f.endsWith(".casm")
5609
+ );
5610
+ if (sierraFiles.length === 0) {
5611
+ throw new Error(`No .sierra.json file found in ${dirPath}. Sierra file is required.`);
5612
+ }
5613
+ if (sierraFiles.length > 1) {
5614
+ throw new Error(
5615
+ `Multiple .sierra.json files found in ${dirPath}: ${sierraFiles.join(", ")}. Please specify which file to use.`
5616
+ );
5617
+ }
5618
+ [sierraFile] = sierraFiles;
5619
+ const baseName = sierraFile.replace(/\.sierra\.json$/, "").replace(/\.json$/, "");
5620
+ casmFile = files.find((f) => f === `${baseName}.casm`);
5621
+ }
5622
+ if (!sierraFile) {
5623
+ throw new Error(`No .sierra.json file found in ${dirPath}. Sierra file is required.`);
5624
+ }
5625
+ const sierraPath = path.join(dirPath, sierraFile);
5626
+ const sierraContent = parse2(fs.readFileSync(sierraPath, "utf8"));
5627
+ if (casmFile) {
5628
+ const casmPath = path.join(dirPath, casmFile);
5629
+ const casmContent = parse2(fs.readFileSync(casmPath, "utf8"));
5630
+ return {
5631
+ sierra: sierraContent,
5632
+ casm: casmContent,
5633
+ compiler: casmContent.compiler_version
5634
+ };
5635
+ }
5636
+ if (!compiledClassHash) {
5637
+ throw new Error(
5638
+ `No .casm file found for ${sierraFile} and no compiledClassHash provided. Either provide a .casm file or pass compiledClassHash as second parameter.`
5639
+ );
5640
+ }
5641
+ return {
5642
+ sierra: sierraContent,
5643
+ compiledClassHash
5644
+ };
5645
+ }
5646
+ async function loadFromFileAPI(input, compiledClassHash) {
5647
+ const files = Array.isArray(input) ? input : [input];
5648
+ if (files.length === 0) {
5649
+ throw new Error("No files provided");
5650
+ }
5651
+ const sierraFiles = files.filter(
5652
+ (file) => file.name.endsWith(".sierra.json") || file.name.endsWith(".json") && !file.name.endsWith(".casm")
5653
+ );
5654
+ const casmFiles = files.filter((file) => file.name.endsWith(".casm"));
5655
+ if (sierraFiles.length > 1) {
5656
+ throw new Error(
5657
+ `Multiple .sierra.json files provided: ${sierraFiles.map((f) => f.name).join(", ")}. Please provide only one sierra file.`
5658
+ );
5659
+ }
5660
+ if (casmFiles.length > 1) {
5661
+ throw new Error(
5662
+ `Multiple .casm files provided: ${casmFiles.map((f) => f.name).join(", ")}. Please provide only one casm file.`
5663
+ );
5664
+ }
5665
+ const sierraFile = sierraFiles[0];
5666
+ const casmFile = casmFiles[0];
5667
+ if (!sierraFile) {
5668
+ throw new Error(
5669
+ `No .sierra.json file found in provided files. Sierra file is required. Provided files: ${files.map((f) => f.name).join(", ")}`
5670
+ );
5671
+ }
5672
+ const sierraContent = parse2(await readFileAsText(sierraFile));
5673
+ if (casmFile) {
5674
+ const casmContent = parse2(await readFileAsText(casmFile));
5675
+ return {
5676
+ sierra: sierraContent,
5677
+ casm: casmContent,
5678
+ compiler: casmContent.compiler_version
5679
+ };
5680
+ }
5681
+ if (!compiledClassHash) {
5682
+ throw new Error(
5683
+ "No .casm file found in provided files and no compiledClassHash provided. Either provide a .casm file or pass compiledClassHash as second parameter."
5684
+ );
5685
+ }
5686
+ return {
5687
+ sierra: sierraContent,
5688
+ compiledClassHash
5689
+ };
5690
+ }
5691
+ function readFileAsText(file) {
5692
+ return new Promise((resolve, reject) => {
5693
+ const reader = new FileReader();
5694
+ reader.onload = (event) => {
5695
+ const content = event.target?.result;
5696
+ if (typeof content === "string") {
5697
+ resolve(content);
5698
+ } else {
5699
+ reject(new Error(`Failed to read file ${file.name} as text`));
5700
+ }
5701
+ };
5702
+ reader.onerror = () => {
5703
+ reject(new Error(`Failed to read file ${file.name}: ${reader.error?.message}`));
5704
+ };
5705
+ reader.readAsText(file);
5706
+ });
5707
+ }
5708
+
5709
+ // src/utils/contract.ts
5710
+ function isSierra(contract) {
5711
+ const compiledContract = isString(contract) ? parse2(contract) : contract;
5712
+ return "sierra_program" in compiledContract;
5713
+ }
5714
+ function extractContractHashes(payload, starknetVersion) {
5715
+ const response = { ...payload };
5716
+ if (isSierra(payload.contract)) {
5717
+ if (!payload.compiledClassHash && payload.casm) {
5718
+ response.compiledClassHash = computeCompiledClassHash(payload.casm, starknetVersion);
5719
+ }
5720
+ if (!response.compiledClassHash)
5721
+ throw new Error(
5722
+ "Extract compiledClassHash failed, provide (CairoAssembly).casm file or compiledClassHash"
5723
+ );
5724
+ }
5725
+ response.classHash = payload.classHash ?? computeContractClassHash(payload.contract);
5726
+ if (!response.classHash)
5727
+ throw new Error("Extract classHash failed, provide (CompiledContract).json file or classHash");
5728
+ return response;
5729
+ }
5730
+ async function contractClassResponseToLegacyCompiledContract(ccr) {
5731
+ if (isSierra(ccr)) {
5732
+ throw Error("ContractClassResponse need to be LegacyContractClass (cairo0 response class)");
5733
+ }
5734
+ const contract = ccr;
5735
+ return {
5736
+ ...contract,
5737
+ program: await decompressProgram(contract.program)
5738
+ };
5739
+ }
5740
+
5741
+ // src/utils/provider.ts
5742
+ function wait(delay) {
5743
+ return new Promise((res) => {
5744
+ setTimeout(res, delay);
5745
+ });
5746
+ }
5747
+ async function createSierraContractClass(contract) {
5748
+ const result = { ...contract };
5749
+ delete result.sierra_program_debug_info;
5750
+ result.abi = formatSpaces(stringify2(contract.abi));
5751
+ result.sierra_program = formatSpaces(stringify2(contract.sierra_program));
5752
+ result.sierra_program = await compressProgram(result.sierra_program);
5753
+ return result;
5754
+ }
5755
+ async function parseContract(contract) {
5756
+ const parsedContract = isString(contract) ? parse2(contract) : contract;
5757
+ if (!isSierra(contract)) {
5758
+ return {
5759
+ ...parsedContract,
5760
+ ..."program" in parsedContract && {
5761
+ program: await compressProgram(parsedContract.program)
5762
+ }
5763
+ };
5764
+ }
5765
+ return await createSierraContractClass(parsedContract);
5766
+ }
5767
+ function extractAbi(contract) {
5768
+ return isString(contract.abi) ? parse2(contract.abi) : contract.abi;
5769
+ }
5770
+ var getDefaultNodeUrl = (networkName, rpcVersion) => {
5771
+ logger.info("Using default public node url, please provide nodeUrl in provider options!");
5772
+ const rpcNodes = getDefaultNodes(rpcVersion ?? config.get("rpcVersion"));
5773
+ const nodes = rpcNodes[networkName ?? _NetworkName.SN_SEPOLIA];
5774
+ const randIdx = Math.floor(Math.random() * nodes.length);
5775
+ return nodes[randIdx];
5776
+ };
5777
+ function getDefaultNodes(rpcVersion) {
5778
+ const apiVersion = toApiVersion(rpcVersion);
5779
+ return Object.fromEntries(
5780
+ Object.entries(RPC_DEFAULT_NODES).map(([key, urls]) => [
5781
+ key,
5782
+ urls.map((url) => `${url}${apiVersion}`)
5783
+ ])
5784
+ );
5785
+ }
5786
+ function getSupportedRpcVersions() {
5787
+ return [...new Set(Object.values(_SupportedRpcVersion))];
5788
+ }
5789
+ var validBlockTags = Object.values(BlockTag);
5790
+ var Block = class {
5791
+ /**
5792
+ * @param {BlockIdentifier} hash if not null, contains the block hash
5793
+ */
5794
+ hash = null;
5795
+ /**
5796
+ * @param {BlockIdentifier} number if not null, contains the block number
5797
+ */
5798
+ number = null;
5799
+ /**
5800
+ * @param {BlockIdentifier} tag if not null, contains "pre_confirmed" or "latest"
5801
+ */
5802
+ tag = null;
5803
+ setIdentifier(__identifier) {
5804
+ if (isString(__identifier)) {
5805
+ if (isDecimalString2(__identifier)) {
5806
+ this.number = parseInt(__identifier, 10);
5807
+ } else if (isHex(__identifier)) {
5808
+ this.hash = __identifier;
5809
+ } else if (validBlockTags.includes(__identifier)) {
5810
+ this.tag = __identifier;
5811
+ } else {
5812
+ throw TypeError(`Block identifier unmanaged: ${__identifier}`);
5813
+ }
5814
+ } else if (isBigInt(__identifier)) {
5815
+ this.hash = toHex(__identifier);
5816
+ } else if (isNumber(__identifier)) {
5817
+ this.number = __identifier;
5818
+ } else {
5819
+ this.tag = BlockTag.LATEST;
5820
+ }
5821
+ if (isNumber(this.number) && this.number < 0) {
5822
+ throw TypeError(`Block number (${this.number}) can't be negative`);
5823
+ }
5824
+ }
5825
+ /**
5826
+ * Create a Block instance
5827
+ * @param {BlockIdentifier} _identifier hex string and BigInt are detected as block hashes.
5828
+ * decimal string and number are detected as block numbers.
5829
+ * text string are detected as block tag.
5830
+ * null is considered as a 'latest' block tag.
5831
+ */
5832
+ constructor(_identifier) {
5833
+ this.setIdentifier(_identifier);
5834
+ }
5835
+ // TODO: fix any
5836
+ /**
5837
+ * @returns {any} the identifier as a string
5838
+ * @example
5839
+ * ```typescript
5840
+ * const result = new provider.Block(123456n).queryIdentifier;
5841
+ * // result = "blockHash=0x1e240"
5842
+ * ```
5843
+ */
5844
+ get queryIdentifier() {
5845
+ if (this.number !== null) {
5846
+ return `blockNumber=${this.number}`;
5847
+ }
5848
+ if (this.hash !== null) {
5849
+ return `blockHash=${this.hash}`;
5850
+ }
5851
+ return `blockNumber=${this.tag}`;
5852
+ }
5853
+ // TODO: fix any
5854
+ /**
5855
+ * @returns {any} the identifier as an object
5856
+ * @example
5857
+ * ```typescript
5858
+ * const result = new provider.Block(56789).identifier;
5859
+ * // result = { block_number: 56789 }
5860
+ * ```
5861
+ */
5862
+ get identifier() {
5863
+ if (this.number !== null) {
5864
+ return { block_number: this.number };
5865
+ }
5866
+ if (this.hash !== null) {
5867
+ return { block_hash: this.hash };
5868
+ }
5869
+ return this.tag;
5870
+ }
5871
+ /**
5872
+ * change the identifier of an existing Block instance
5873
+ * @example
5874
+ * ```typescript
5875
+ * const myBlock = new provider.Block("latest");
5876
+ * myBlock.identifier ="0x3456789abc";
5877
+ * const result = myBlock.identifier;
5878
+ * // result = { block_hash: '0x3456789abc' }
5879
+ * ```
5880
+ */
5881
+ set identifier(_identifier) {
5882
+ this.setIdentifier(_identifier);
5883
+ }
5884
+ valueOf = () => this.number;
5885
+ toString = () => this.hash;
5886
+ };
5887
+
5888
+ // src/utils/eventEmitter.ts
5889
+ var EventEmitter = class {
5890
+ listeners = {};
5891
+ on(event, listener) {
5892
+ if (!this.listeners[event]) {
5893
+ this.listeners[event] = [];
5894
+ }
5895
+ this.listeners[event].push(listener);
5896
+ }
5897
+ off(event, listener) {
5898
+ if (!this.listeners[event]) {
5899
+ return;
5900
+ }
5901
+ this.listeners[event] = this.listeners[event].filter((l) => l !== listener);
5902
+ }
5903
+ emit(event, data) {
5904
+ if (this.listeners[event]) {
5905
+ this.listeners[event].forEach((listener) => listener(data));
5906
+ }
5907
+ }
5908
+ clear() {
5909
+ this.listeners = {};
5910
+ }
5911
+ };
5912
+
5913
+ // src/channel/ws/subscription.ts
5914
+ var Subscription = class {
5915
+ /**
5916
+ * The channel that created this subscription.
5917
+ * @internal
5918
+ */
5919
+ channel;
5920
+ /**
5921
+ * The JSON-RPC method used to create this subscription.
5922
+ * @internal
5923
+ */
5924
+ method;
5925
+ /**
5926
+ * The parameters used to create this subscription.
5927
+ * @internal
5928
+ */
5929
+ params;
5930
+ /**
5931
+ * The unique identifier for this subscription.
5932
+ * @internal
5933
+ */
5934
+ id;
5935
+ events = new EventEmitter();
5936
+ buffer = [];
5937
+ maxBufferSize;
5938
+ handler = null;
5939
+ _isClosed = false;
5940
+ // The unsubscribe request currently on the wire, shared by concurrent callers.
5941
+ pendingUnsubscribe = null;
5942
+ /**
5943
+ * @internal
5944
+ * @param options - Subscription configuration options
5945
+ */
5946
+ constructor(options) {
5947
+ this.channel = options.channel;
5948
+ this.method = options.method;
5949
+ this.params = options.params ?? {};
5950
+ this.id = options.id;
5951
+ this.maxBufferSize = options.maxBufferSize;
5952
+ }
5953
+ /**
5954
+ * Indicates if the subscription has been closed.
5955
+ * @returns {boolean} `true` if unsubscribed, `false` otherwise.
5956
+ */
5957
+ get isClosed() {
5958
+ return this._isClosed;
5959
+ }
5960
+ /**
5961
+ * Registers a listener for this subscription's closure, whatever causes it: an explicit
5962
+ * `unsubscribe()`, or the channel giving up on re-establishing it after a reconnection.
5963
+ *
5964
+ * The second case is the reason this exists. A subscription killed by a refused restore stops
5965
+ * delivering while the connection stays up, so the transport's `statechange` says nothing about
5966
+ * it and a consumer would wait forever on events that can no longer come.
5967
+ *
5968
+ * @param listener - Called once, when the subscription closes.
5969
+ * @returns the detach function, so the pair matches `useSyncExternalStore`'s subscribe contract.
5970
+ */
5971
+ onClose(listener) {
5972
+ if (this._isClosed) {
5973
+ listener();
5974
+ return () => {
5975
+ };
5976
+ }
5977
+ this.events.on("unsubscribe", listener);
5978
+ return () => {
5979
+ this.events.off("unsubscribe", listener);
5980
+ };
5981
+ }
5982
+ /**
5983
+ * Closes the subscription locally, without contacting the node.
5984
+ *
5985
+ * Used when the channel knows the subscription is gone and cannot be recovered — a
5986
+ * re-subscribe refused after a reconnection, for instance. Without it the object would keep
5987
+ * reporting itself as open while no event could ever reach its handler again.
5988
+ * @internal
5989
+ */
5990
+ _markClosed() {
5991
+ if (this._isClosed) return;
5992
+ this._isClosed = true;
5993
+ this.handler = null;
5994
+ this.events.emit("unsubscribe", void 0);
5995
+ this.events.clear();
5996
+ }
5997
+ /**
5998
+ * Internal method to handle incoming events from the WebSocket channel.
5999
+ * If a handler is attached, it's invoked immediately. Otherwise, the event is buffered.
6000
+ * @internal
6001
+ * @param {T} data - The event data.
6002
+ */
6003
+ _handleEvent(data) {
6004
+ if (this.handler) {
6005
+ this.handler(data);
6006
+ } else {
6007
+ if (this.buffer.length >= this.maxBufferSize) {
6008
+ const droppedEvent = this.buffer.shift();
6009
+ logger.warn(`Subscription ${this.id}: Buffer full. Dropping oldest event:`, droppedEvent);
6010
+ }
6011
+ this.buffer.push(data);
6012
+ }
6013
+ }
6014
+ /**
6015
+ * Attaches a handler function to be called for each event.
6016
+ *
6017
+ * When a handler is attached, any buffered events will be passed to it sequentially.
6018
+ * Subsequent events will be passed directly as they arrive.
6019
+ *
6020
+ * Re-attaching the **same** handler is a no-op: React StrictMode invokes an effect twice, and
6021
+ * throwing there would fail a correct component. A *different* handler while one is attached is
6022
+ * still refused — that is a genuine mistake, not a lifecycle artefact.
6023
+ *
6024
+ * @param {(data: T) => void} handler - The function to call with event data.
6025
+ * @throws {Error} If a different handler is already attached to this subscription.
6026
+ */
6027
+ on(handler) {
6028
+ if (this._isClosed) return;
6029
+ if (this.handler === handler) return;
6030
+ if (this.handler) {
6031
+ throw new Error("A handler is already attached to this subscription.");
6032
+ }
6033
+ this.handler = handler;
6034
+ while (this.buffer.length > 0) {
6035
+ const event = this.buffer.shift();
6036
+ if (event) {
6037
+ this.handler(event);
6038
+ }
6039
+ }
6040
+ }
6041
+ /**
6042
+ * Detaches the current handler.
6043
+ *
6044
+ * Events arriving afterwards are buffered again, so a later `on()` receives them: the
6045
+ * subscription itself is untouched, only the delivery is paused. This is what a React effect
6046
+ * cleanup needs — dropping the handler without tearing down a subscription the component may
6047
+ * re-attach to on its next mount.
6048
+ */
6049
+ off() {
6050
+ this.handler = null;
6051
+ }
6052
+ /**
6053
+ * Sends an unsubscribe request to the node and cleans up local resources.
6054
+ * @returns {Promise<boolean>} A Promise that resolves to `true` if the unsubscription was successful.
6055
+ */
6056
+ async unsubscribe() {
6057
+ if (this._isClosed) {
6058
+ return true;
6059
+ }
6060
+ if (this.pendingUnsubscribe) {
6061
+ return this.pendingUnsubscribe;
5579
6062
  }
5580
- return obj;
6063
+ this.pendingUnsubscribe = (async () => {
6064
+ try {
6065
+ const success = await this.channel.unsubscribe(this.id);
6066
+ if (success) {
6067
+ this._isClosed = true;
6068
+ this.handler = null;
6069
+ this.channel.removeSubscription(this.id);
6070
+ this.events.emit("unsubscribe", void 0);
6071
+ this.events.clear();
6072
+ }
6073
+ return success;
6074
+ } finally {
6075
+ this.pendingUnsubscribe = null;
6076
+ }
6077
+ })();
6078
+ return this.pendingUnsubscribe;
6079
+ }
6080
+ };
6081
+
6082
+ // src/channel/ws/subscriptionChannel.ts
6083
+ var isReconnectAware = (transport) => typeof transport.onReconnected === "function";
6084
+ var SubscriptionChannel = class {
6085
+ id = "RPC0.10.2-subscriptions";
6086
+ channelSpecVersion = _SupportedRpcVersion.v0_10_2;
6087
+ transport;
6088
+ maxBufferSize;
6089
+ /** Live subscriptions, keyed by the id the node issued. Always a string — see spec §6. */
6090
+ activeSubscriptions = /* @__PURE__ */ new Map();
6091
+ /**
6092
+ * Callers blocked in `waitForUnsubscription`, keyed by subscription id.
6093
+ *
6094
+ * Held here rather than as event listeners because an `unsubscribe` event only ever announces
6095
+ * success: a waiter attached to it cannot learn that the node refused, or that the connection
6096
+ * went away, and would wait forever with no timeout of its own to fall back on.
6097
+ */
6098
+ unsubscribeWaiters = /* @__PURE__ */ new Map();
6099
+ /**
6100
+ * Events that arrived before their subscription finished registering.
6101
+ *
6102
+ * The node can push the first event in the same burst as the reply carrying the subscription
6103
+ * id, while the continuation that registers the handle has not run yet. Holding the event and
6104
+ * handing it over at registration is deterministic; the alternative — waiting a fixed number
6105
+ * of microtasks — only works for one particular call depth and breaks silently when a layer
6106
+ * is added.
6107
+ */
6108
+ earlyEvents = /* @__PURE__ */ new Map();
6109
+ /**
6110
+ * Subscribe requests currently on the wire.
6111
+ *
6112
+ * An unknown subscription id is only worth holding while one of these is outstanding.
6113
+ * Otherwise it belongs to a subscription that is already gone — the node pushing one last
6114
+ * event after an unsubscribe — and holding it would leak.
6115
+ */
6116
+ pendingSubscribes = 0;
6117
+ /** Its own request numbering, renumbered by the transport before it reaches the wire. */
6118
+ requestId = 0;
6119
+ detachNotifications;
6120
+ detachState;
6121
+ detachReconnected = () => {
5581
6122
  };
5582
- return convertBigIntToHex(resourceBoundsBN);
5583
- }
5584
- function resourceBoundsToBigInt(resourceBounds) {
5585
- const convertStringToBigInt = (obj) => {
5586
- if (isString(obj)) {
5587
- return BigInt(obj);
6123
+ constructor(options) {
6124
+ this.transport = options.transport;
6125
+ this.maxBufferSize = options.maxBufferSize ?? 1e3;
6126
+ this.detachNotifications = this.transport.on("notification", this.handleNotification);
6127
+ this.detachState = this.transport.on("statechange", this.handleStateChange);
6128
+ if (isReconnectAware(this.transport)) {
6129
+ this.detachReconnected = this.transport.onReconnected(() => this.restore());
5588
6130
  }
5589
- if (isObject(obj)) {
5590
- const result = {};
5591
- Object.keys(obj).forEach((key) => {
5592
- result[key] = convertStringToBigInt(obj[key]);
6131
+ }
6132
+ /**
6133
+ * Sends one JSON-RPC call and returns its result, applying the same error contract as
6134
+ * `RpcChannel`: a protocol error becomes a typed `RpcError`.
6135
+ */
6136
+ async send(method, params) {
6137
+ this.requestId += 1;
6138
+ const response = await this.transport.request({
6139
+ id: this.requestId,
6140
+ jsonrpc: "2.0",
6141
+ method,
6142
+ ...params && { params }
6143
+ });
6144
+ if (response.error) {
6145
+ throw new RpcError(response.error, method, params);
6146
+ }
6147
+ return response.result;
6148
+ }
6149
+ /** Registers a new subscription and returns the handle the caller keeps. */
6150
+ async openSubscription(method, rpcParams) {
6151
+ this.pendingSubscribes += 1;
6152
+ try {
6153
+ const subId = await this.send(method, rpcParams);
6154
+ const subscription = new Subscription({
6155
+ channel: this,
6156
+ method,
6157
+ params: rpcParams,
6158
+ id: subId,
6159
+ maxBufferSize: this.maxBufferSize
5593
6160
  });
5594
- return result;
6161
+ this.activeSubscriptions.set(subId, subscription);
6162
+ const early = this.earlyEvents.get(subId);
6163
+ if (early) {
6164
+ this.earlyEvents.delete(subId);
6165
+ early.forEach((event) => subscription._handleEvent(event));
6166
+ }
6167
+ return subscription;
6168
+ } finally {
6169
+ this.pendingSubscribes -= 1;
6170
+ if (this.pendingSubscribes === 0 && this.earlyEvents.size > 0) {
6171
+ this.earlyEvents.forEach(
6172
+ (_events, id) => logger.warn(`SubscriptionChannel: Received event for untracked subscription ID: ${id}.`)
6173
+ );
6174
+ this.earlyEvents.clear();
6175
+ }
5595
6176
  }
5596
- return obj;
5597
- };
5598
- return convertStringToBigInt(resourceBounds);
5599
- }
5600
-
5601
- // src/utils/contractLoader.ts
5602
- function isFileSystemAvailable() {
5603
- try {
5604
- return typeof __require !== "undefined" && typeof __require.resolve === "function";
5605
- } catch {
5606
- logger.info("isFileSystemAvailable: false");
5607
- return false;
5608
6177
  }
5609
- }
5610
- function contractLoader(input, compiledClassHash) {
5611
- if (typeof File !== "undefined" && (input instanceof File || Array.isArray(input))) {
5612
- return loadFromFileAPI(input, compiledClassHash);
6178
+ /**
6179
+ * Subscribes to new block headers.
6180
+ * @param params - Where to start from. Defaults to the latest block; a block number or hash
6181
+ * replays from there, up to 1024 blocks back.
6182
+ * @returns A `Subscription` delivering one block header per new block.
6183
+ * @example
6184
+ * ```typescript
6185
+ * const sub = await channel.subscribeNewHeads();
6186
+ * sub.on((header) => console.log(header.block_number, header.block_hash));
6187
+ * ```
6188
+ */
6189
+ async subscribeNewHeads(params = {}) {
6190
+ const method = "starknet_subscribeNewHeads";
6191
+ const rpcParams = {
6192
+ block_id: params.blockIdentifier ? new Block(params.blockIdentifier).identifier : void 0
6193
+ };
6194
+ return this.openSubscription(method, rpcParams);
5613
6195
  }
5614
- if (typeof input === "string") {
5615
- if (!isFileSystemAvailable()) {
5616
- throw new Error(
5617
- 'contractLoader with string paths is only available in Node.js environments. In browsers, please use File objects from <input type="file"> or drag-and-drop. Example: await contractLoader(fileInput.files[0])'
5618
- );
6196
+ /**
6197
+ * Subscribes to events matching a given filter.
6198
+ * @param params - Filters on the emitting address, the event keys, the starting block and the
6199
+ * finality status. All are optional: without any, every event of the network is delivered.
6200
+ * @returns A `Subscription` delivering one emitted event at a time.
6201
+ * @example
6202
+ * ```typescript
6203
+ * const sub = await channel.subscribeEvents({
6204
+ * fromAddress: '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7',
6205
+ * finalityStatus: 'ACCEPTED_ON_L2',
6206
+ * });
6207
+ * sub.on((event) => console.log(event.from_address, event.keys, event.data));
6208
+ * ```
6209
+ */
6210
+ async subscribeEvents(params = {}) {
6211
+ const method = "starknet_subscribeEvents";
6212
+ let from_address;
6213
+ if (params.fromAddress !== void 0) {
6214
+ from_address = Array.isArray(params.fromAddress) ? bigNumberishArrayToHexadecimalStringArray(params.fromAddress) : toHex(params.fromAddress);
5619
6215
  }
5620
- return loadFromFileSystem(input, compiledClassHash);
6216
+ const rpcParams = {
6217
+ from_address,
6218
+ keys: params.keys,
6219
+ block_id: params.blockIdentifier ? new Block(params.blockIdentifier).identifier : void 0,
6220
+ finality_status: params.finalityStatus
6221
+ };
6222
+ return this.openSubscription(method, rpcParams);
5621
6223
  }
5622
- throw new Error(
5623
- "Invalid input type. Expected string (Node.js path) or File/File[] (browser File API)"
5624
- );
5625
- }
5626
- function loadFromFileSystem(contractPath, compiledClassHash) {
5627
- const fs = __require("fs");
5628
- const path = __require("path");
5629
- const resolvedPath = path.resolve(contractPath);
5630
- let dirPath;
5631
- let specifiedSierraFile;
5632
- let specifiedCasmFile;
5633
- const stats = fs.statSync(resolvedPath);
5634
- if (stats.isFile()) {
5635
- dirPath = path.dirname(resolvedPath);
5636
- const fileName = path.basename(resolvedPath);
5637
- if (fileName.endsWith(".sierra.json") || fileName.endsWith(".json") && !fileName.endsWith(".casm")) {
5638
- specifiedSierraFile = fileName;
5639
- } else if (fileName.endsWith(".casm")) {
5640
- specifiedCasmFile = fileName;
5641
- } else {
5642
- throw new Error(
5643
- `Invalid file type. Expected .json, .sierra.json, or .casm file, got: ${fileName}`
6224
+ /**
6225
+ * Subscribes to status updates for a specific transaction.
6226
+ * @param params - The `transactionHash` to follow, and optionally the block to start from.
6227
+ * @returns A `Subscription` delivering the transaction's current status, then every change.
6228
+ * @example
6229
+ * ```typescript
6230
+ * const sub = await channel.subscribeTransactionStatus({ transactionHash: '0x0123...' });
6231
+ * sub.on((update) => console.log(update.status.finality_status));
6232
+ * ```
6233
+ */
6234
+ async subscribeTransactionStatus(params) {
6235
+ const method = "starknet_subscribeTransactionStatus";
6236
+ const rpcParams = {
6237
+ transaction_hash: toHex(params.transactionHash),
6238
+ block_id: params.blockIdentifier ? new Block(params.blockIdentifier).identifier : void 0
6239
+ };
6240
+ return this.openSubscription(method, rpcParams);
6241
+ }
6242
+ /**
6243
+ * Subscribes to new transaction receipts.
6244
+ *
6245
+ * Same filters as {@link subscribeNewTransactions}, but the full receipt is delivered instead of
6246
+ * the transaction.
6247
+ * @param params - Optional filters on the finality status and on the sender addresses.
6248
+ * @returns A `Subscription` delivering one receipt per matching transaction.
6249
+ * @example
6250
+ * ```typescript
6251
+ * const sub = await channel.subscribeNewTransactionReceipts({
6252
+ * finalityStatus: ['ACCEPTED_ON_L2'],
6253
+ * });
6254
+ * sub.on((receipt) => console.log(receipt.transaction_hash, receipt.execution_status));
6255
+ * ```
6256
+ */
6257
+ async subscribeNewTransactionReceipts(params = {}) {
6258
+ const method = "starknet_subscribeNewTransactionReceipts";
6259
+ const rpcParams = {
6260
+ finality_status: params.finalityStatus,
6261
+ sender_address: params.senderAddress && bigNumberishArrayToHexadecimalStringArray(params.senderAddress)
6262
+ };
6263
+ return this.openSubscription(method, rpcParams);
6264
+ }
6265
+ /**
6266
+ * Subscribes to new transactions and to their finality status changes.
6267
+ *
6268
+ * One event is fired per status update, so the same transaction can be delivered several times.
6269
+ * @param params - Optional filters on the finality status (defaults to `['ACCEPTED_ON_L2']`) and
6270
+ * on the sender addresses.
6271
+ * @returns A `Subscription` delivering one transaction per matching status update.
6272
+ * @example
6273
+ * ```typescript
6274
+ * const sub = await channel.subscribeNewTransactions({
6275
+ * finalityStatus: ['RECEIVED', 'ACCEPTED_ON_L2'],
6276
+ * });
6277
+ * sub.on((tx) => console.log(tx.transaction_hash, tx.finality_status));
6278
+ * ```
6279
+ */
6280
+ async subscribeNewTransactions(params = {}) {
6281
+ const method = "starknet_subscribeNewTransactions";
6282
+ const rpcParams = {
6283
+ finality_status: params.finalityStatus,
6284
+ sender_address: params.senderAddress && bigNumberishArrayToHexadecimalStringArray(params.senderAddress),
6285
+ tags: params.tags
6286
+ };
6287
+ return this.openSubscription(method, rpcParams);
6288
+ }
6289
+ handleNotification = (notification) => {
6290
+ const { subscription_id: subscriptionId, result } = notification.params;
6291
+ if (subscriptionId === void 0) return;
6292
+ const subscription = this.activeSubscriptions.get(subscriptionId);
6293
+ if (subscription) {
6294
+ subscription._handleEvent(result);
6295
+ return;
6296
+ }
6297
+ if (this.pendingSubscribes === 0) {
6298
+ logger.warn(
6299
+ `SubscriptionChannel: Received event for untracked subscription ID: ${subscriptionId}.`
5644
6300
  );
6301
+ return;
5645
6302
  }
5646
- } else if (stats.isDirectory()) {
5647
- dirPath = resolvedPath;
5648
- } else {
5649
- throw new Error(`Path is neither a file nor a directory: ${contractPath}`);
5650
- }
5651
- const files = fs.readdirSync(dirPath);
5652
- let sierraFile;
5653
- let casmFile;
5654
- if (specifiedSierraFile) {
5655
- sierraFile = specifiedSierraFile;
5656
- const baseName = sierraFile.replace(/\.sierra\.json$/, "").replace(/\.json$/, "");
5657
- casmFile = files.find((f) => f === `${baseName}.casm`);
5658
- } else if (specifiedCasmFile) {
5659
- casmFile = specifiedCasmFile;
5660
- const baseName = casmFile.replace(/\.casm$/, "");
5661
- sierraFile = files.find((f) => f === `${baseName}.sierra.json`) || files.find((f) => f === `${baseName}.json`);
5662
- } else {
5663
- const sierraFiles = files.filter(
5664
- (f) => f.endsWith(".sierra.json") || f.endsWith(".json") && !f.endsWith(".casm")
6303
+ const buffered = this.earlyEvents.get(subscriptionId) ?? [];
6304
+ buffered.push(result);
6305
+ this.earlyEvents.set(subscriptionId, buffered);
6306
+ };
6307
+ handleStateChange = () => {
6308
+ const state = this.transport.getState();
6309
+ if (state === "open" || state === "connecting") return;
6310
+ this.rejectUnsubscribeWaiters("the connection was closed");
6311
+ };
6312
+ /**
6313
+ * Re-issues every live subscription on a freshly reconnected socket.
6314
+ *
6315
+ * A subscription that cannot be re-established is **not** put back in the map, and its handle
6316
+ * is closed: otherwise it would keep reporting itself as live while nothing could ever reach
6317
+ * its handler again.
6318
+ */
6319
+ async restore() {
6320
+ const previous = Array.from(this.activeSubscriptions.values());
6321
+ this.activeSubscriptions.clear();
6322
+ await Promise.all(
6323
+ previous.map(async (subscription) => {
6324
+ try {
6325
+ const newId = await this.send(subscription.method, subscription.params);
6326
+ subscription.id = newId;
6327
+ this.activeSubscriptions.set(newId, subscription);
6328
+ logger.info(`Subscription ${subscription.method} restored with new ID: ${newId}`);
6329
+ } catch (error) {
6330
+ logger.error(`Failed to restore subscription ${subscription.method}:`, error);
6331
+ subscription._markClosed();
6332
+ }
6333
+ })
5665
6334
  );
5666
- if (sierraFiles.length === 0) {
5667
- throw new Error(`No .sierra.json file found in ${dirPath}. Sierra file is required.`);
6335
+ }
6336
+ /**
6337
+ * Unsubscribes from a Starknet subscription.
6338
+ *
6339
+ * Prefer `subscription.unsubscribe()`.
6340
+ * @internal
6341
+ */
6342
+ async unsubscribe(subscriptionId) {
6343
+ let status;
6344
+ try {
6345
+ status = await this.send("starknet_unsubscribe", {
6346
+ subscription_id: subscriptionId
6347
+ });
6348
+ } catch (error) {
6349
+ this.settleUnsubscribeWaiters(subscriptionId, error);
6350
+ throw error;
5668
6351
  }
5669
- if (sierraFiles.length > 1) {
5670
- throw new Error(
5671
- `Multiple .sierra.json files found in ${dirPath}: ${sierraFiles.join(", ")}. Please specify which file to use.`
6352
+ if (status) {
6353
+ this.settleUnsubscribeWaiters(subscriptionId);
6354
+ } else {
6355
+ this.settleUnsubscribeWaiters(
6356
+ subscriptionId,
6357
+ new Error(`Node refused to unsubscribe subscription ${subscriptionId}`)
5672
6358
  );
5673
6359
  }
5674
- [sierraFile] = sierraFiles;
5675
- const baseName = sierraFile.replace(/\.sierra\.json$/, "").replace(/\.json$/, "");
5676
- casmFile = files.find((f) => f === `${baseName}.casm`);
6360
+ return status;
5677
6361
  }
5678
- if (!sierraFile) {
5679
- throw new Error(`No .sierra.json file found in ${dirPath}. Sierra file is required.`);
6362
+ /**
6363
+ * Removes a subscription from the active map.
6364
+ * @internal
6365
+ */
6366
+ removeSubscription(subscriptionId) {
6367
+ this.activeSubscriptions.delete(subscriptionId);
6368
+ }
6369
+ /**
6370
+ * The live subscriptions, keyed by the id the node issued.
6371
+ * @internal Exposed for the `WebSocketChannel` compatibility façade.
6372
+ */
6373
+ get subscriptions() {
6374
+ return this.activeSubscriptions;
6375
+ }
6376
+ /** Resolves when a specific subscription is successfully unsubscribed. */
6377
+ waitForUnsubscription(targetId) {
6378
+ return new Promise((resolve, reject) => {
6379
+ const waiters = this.unsubscribeWaiters.get(targetId) ?? /* @__PURE__ */ new Set();
6380
+ waiters.add({ resolve, reject });
6381
+ this.unsubscribeWaiters.set(targetId, waiters);
6382
+ });
5680
6383
  }
5681
- const sierraPath = path.join(dirPath, sierraFile);
5682
- const sierraContent = parse2(fs.readFileSync(sierraPath, "utf8"));
5683
- if (casmFile) {
5684
- const casmPath = path.join(dirPath, casmFile);
5685
- const casmContent = parse2(fs.readFileSync(casmPath, "utf8"));
5686
- return {
5687
- sierra: sierraContent,
5688
- casm: casmContent,
5689
- compiler: casmContent.compiler_version
5690
- };
6384
+ settleUnsubscribeWaiters(subscriptionId, error) {
6385
+ const waiters = this.unsubscribeWaiters.get(subscriptionId);
6386
+ if (!waiters) return;
6387
+ this.unsubscribeWaiters.delete(subscriptionId);
6388
+ waiters.forEach((waiter) => error ? waiter.reject(error) : waiter.resolve());
5691
6389
  }
5692
- if (!compiledClassHash) {
5693
- throw new Error(
5694
- `No .casm file found for ${sierraFile} and no compiledClassHash provided. Either provide a .casm file or pass compiledClassHash as second parameter.`
6390
+ rejectUnsubscribeWaiters(reason) {
6391
+ if (this.unsubscribeWaiters.size === 0) return;
6392
+ Array.from(this.unsubscribeWaiters.keys()).forEach(
6393
+ (id) => this.settleUnsubscribeWaiters(
6394
+ id,
6395
+ new WebSocketNotConnectedError(`Subscription ${id} was never unsubscribed: ${reason}`)
6396
+ )
5695
6397
  );
5696
6398
  }
5697
- return {
5698
- sierra: sierraContent,
5699
- compiledClassHash
5700
- };
6399
+ /**
6400
+ * Stops listening to the transport and settles anything still waiting.
6401
+ *
6402
+ * Does **not** close the socket: this channel borrowed it.
6403
+ */
6404
+ close() {
6405
+ this.detachNotifications();
6406
+ this.detachState();
6407
+ this.detachReconnected();
6408
+ this.rejectUnsubscribeWaiters("the subscription channel was closed");
6409
+ this.activeSubscriptions.forEach((subscription) => subscription._markClosed());
6410
+ this.activeSubscriptions.clear();
6411
+ this.earlyEvents.clear();
6412
+ }
6413
+ };
6414
+
6415
+ // src/channel/transport/http.ts
6416
+ function postJsonRpc(options, body) {
6417
+ return options.baseFetch(options.nodeUrl, {
6418
+ method: "POST",
6419
+ body: stringify2(body),
6420
+ headers: options.headers
6421
+ });
5701
6422
  }
5702
- async function loadFromFileAPI(input, compiledClassHash) {
5703
- const files = Array.isArray(input) ? input : [input];
5704
- if (files.length === 0) {
5705
- throw new Error("No files provided");
6423
+ var HttpTransport = class {
6424
+ options;
6425
+ constructor(options) {
6426
+ this.options = options;
5706
6427
  }
5707
- const sierraFiles = files.filter(
5708
- (file) => file.name.endsWith(".sierra.json") || file.name.endsWith(".json") && !file.name.endsWith(".casm")
5709
- );
5710
- const casmFiles = files.filter((file) => file.name.endsWith(".casm"));
5711
- if (sierraFiles.length > 1) {
5712
- throw new Error(
5713
- `Multiple .sierra.json files provided: ${sierraFiles.map((f) => f.name).join(", ")}. Please provide only one sierra file.`
5714
- );
6428
+ async request(body) {
6429
+ const response = await postJsonRpc(this.options, body);
6430
+ return response.json();
5715
6431
  }
5716
- if (casmFiles.length > 1) {
5717
- throw new Error(
5718
- `Multiple .casm files provided: ${casmFiles.map((f) => f.name).join(", ")}. Please provide only one casm file.`
6432
+ };
6433
+
6434
+ // src/utils/connect/ws.ts
6435
+ var ws_default = typeof WebSocket !== "undefined" && WebSocket || typeof globalThis !== "undefined" && globalThis.WebSocket || typeof window !== "undefined" && window.WebSocket && window.WebSocket.bind(window) || typeof global !== "undefined" && global.WebSocket || class {
6436
+ constructor() {
6437
+ throw new LibraryError(
6438
+ "WebSocket module not detected, use the 'websocket' constructor parameter to set a compatible connection"
5719
6439
  );
5720
6440
  }
5721
- const sierraFile = sierraFiles[0];
5722
- const casmFile = casmFiles[0];
5723
- if (!sierraFile) {
5724
- throw new Error(
5725
- `No .sierra.json file found in provided files. Sierra file is required. Provided files: ${files.map((f) => f.name).join(", ")}`
5726
- );
6441
+ };
6442
+
6443
+ // src/channel/transport/ws.ts
6444
+ var WsTransport = class {
6445
+ /** The URL of the WebSocket RPC node. */
6446
+ nodeUrl;
6447
+ /**
6448
+ * The underlying socket. Owned by this transport — close it through `close()`.
6449
+ *
6450
+ * Not `readonly` because a subclass may replace it on a reconnection. This class never does:
6451
+ * one `WsTransport` is one socket, which is what its own tests rely on.
6452
+ */
6453
+ socket;
6454
+ /** Kept so a subclass can build a replacement socket the same way this one was built. */
6455
+ WsImplementation;
6456
+ requestTimeout;
6457
+ state = "connecting";
6458
+ listeners = {
6459
+ statechange: /* @__PURE__ */ new Set(),
6460
+ notification: /* @__PURE__ */ new Set(),
6461
+ open: /* @__PURE__ */ new Set(),
6462
+ close: /* @__PURE__ */ new Set(),
6463
+ error: /* @__PURE__ */ new Set(),
6464
+ message: /* @__PURE__ */ new Set()
6465
+ };
6466
+ /**
6467
+ * Resolves once the socket is open, rejects if the connection never comes up.
6468
+ *
6469
+ * Created on first use rather than in the constructor, so the constructor starts no
6470
+ * asynchronous work and a failed connection rejects inside a call the caller can wrap in
6471
+ * `try/catch` instead of becoming an unhandled rejection.
6472
+ */
6473
+ ready;
6474
+ /** One entry per request on the wire, keyed by the id this transport put on it. */
6475
+ pending = /* @__PURE__ */ new Map();
6476
+ /**
6477
+ * Wire-level request counter.
6478
+ *
6479
+ * Owned here rather than by the channels: each channel starts numbering at 1, so two of them
6480
+ * sharing one socket would both emit `1` and get cross-matched replies — silently, and
6481
+ * non-deterministically.
6482
+ */
6483
+ wireId = 0;
6484
+ constructor(options) {
6485
+ this.nodeUrl = options.nodeUrl;
6486
+ this.requestTimeout = options.requestTimeout ?? 6e4;
6487
+ this.WsImplementation = options.websocket || config.get("websocket") || ws_default;
6488
+ this.socket = new this.WsImplementation(this.nodeUrl);
6489
+ if (this.socket.readyState === ws_default.OPEN) this.state = "open";
6490
+ this.attach(this.socket);
5727
6491
  }
5728
- const sierraContent = parse2(await readFileAsText(sierraFile));
5729
- if (casmFile) {
5730
- const casmContent = parse2(await readFileAsText(casmFile));
5731
- return {
5732
- sierra: sierraContent,
5733
- casm: casmContent,
5734
- compiler: casmContent.compiler_version
5735
- };
6492
+ attach(socket) {
6493
+ socket.addEventListener("open", this.handleOpen);
6494
+ socket.addEventListener("message", this.handleMessage);
6495
+ socket.addEventListener("close", this.handleClose);
6496
+ socket.addEventListener("error", this.handleError);
5736
6497
  }
5737
- if (!compiledClassHash) {
5738
- throw new Error(
5739
- "No .casm file found in provided files and no compiledClassHash provided. Either provide a .casm file or pass compiledClassHash as second parameter."
5740
- );
6498
+ detach(socket) {
6499
+ socket.removeEventListener("open", this.handleOpen);
6500
+ socket.removeEventListener("message", this.handleMessage);
6501
+ socket.removeEventListener("close", this.handleClose);
6502
+ socket.removeEventListener("error", this.handleError);
5741
6503
  }
5742
- return {
5743
- sierra: sierraContent,
5744
- compiledClassHash
6504
+ /**
6505
+ * Adopts a replacement socket.
6506
+ *
6507
+ * The readiness latch is discarded with the old socket: it records that *that* connection came
6508
+ * up, and reusing it would let a request be written to a socket still handshaking.
6509
+ *
6510
+ * Requests already on the wire are not settled here — `handleClose` has done that for the
6511
+ * socket that went away, since nothing on a dead socket can still be answered.
6512
+ */
6513
+ swapSocket(next) {
6514
+ this.detach(this.socket);
6515
+ this.socket = next;
6516
+ this.ready = void 0;
6517
+ this.attach(next);
6518
+ }
6519
+ handleOpen = (event) => {
6520
+ this.onOpened();
6521
+ this.emit("open", event);
5745
6522
  };
5746
- }
5747
- function readFileAsText(file) {
5748
- return new Promise((resolve, reject) => {
5749
- const reader = new FileReader();
5750
- reader.onload = (event) => {
5751
- const content = event.target?.result;
5752
- if (typeof content === "string") {
5753
- resolve(content);
5754
- } else {
5755
- reject(new Error(`Failed to read file ${file.name} as text`));
5756
- }
5757
- };
5758
- reader.onerror = () => {
5759
- reject(new Error(`Failed to read file ${file.name}: ${reader.error?.message}`));
5760
- };
5761
- reader.readAsText(file);
5762
- });
5763
- }
5764
-
5765
- // src/utils/contract.ts
5766
- function isSierra(contract) {
5767
- const compiledContract = isString(contract) ? parse2(contract) : contract;
5768
- return "sierra_program" in compiledContract;
5769
- }
5770
- function extractContractHashes(payload, starknetVersion) {
5771
- const response = { ...payload };
5772
- if (isSierra(payload.contract)) {
5773
- if (!payload.compiledClassHash && payload.casm) {
5774
- response.compiledClassHash = computeCompiledClassHash(payload.casm, starknetVersion);
5775
- }
5776
- if (!response.compiledClassHash)
5777
- throw new Error(
5778
- "Extract compiledClassHash failed, provide (CairoAssembly).casm file or compiledClassHash"
5779
- );
6523
+ handleClose = (event) => {
6524
+ this.detach(this.socket);
6525
+ this.rejectPending("the connection was closed");
6526
+ this.onClosed(event);
6527
+ this.emit("close", event);
6528
+ };
6529
+ /**
6530
+ * What happens once a socket is open. Overridden by a reconnecting subclass, which also has a
6531
+ * retry counter to reset and a queue to flush.
6532
+ */
6533
+ onOpened() {
6534
+ this.setState("open");
5780
6535
  }
5781
- response.classHash = payload.classHash ?? computeContractClassHash(payload.contract);
5782
- if (!response.classHash)
5783
- throw new Error("Extract classHash failed, provide (CompiledContract).json file or classHash");
5784
- return response;
5785
- }
5786
- async function contractClassResponseToLegacyCompiledContract(ccr) {
5787
- if (isSierra(ccr)) {
5788
- throw Error("ContractClassResponse need to be LegacyContractClass (cairo0 response class)");
6536
+ /**
6537
+ * What happens once a socket is gone. This class stays closed; a reconnecting subclass starts
6538
+ * an attempt instead.
6539
+ */
6540
+ onClosed(_event) {
6541
+ this.setState("closed");
5789
6542
  }
5790
- const contract = ccr;
5791
- return {
5792
- ...contract,
5793
- program: await decompressProgram(contract.program)
6543
+ handleMessage = (event) => {
6544
+ if (!isString(event.data)) {
6545
+ logger.warn("WsTransport: received non-string message data:", event.data);
6546
+ return;
6547
+ }
6548
+ let parsed;
6549
+ try {
6550
+ parsed = JSON.parse(event.data);
6551
+ } catch (error) {
6552
+ logger.error(`WsTransport: error parsing incoming message: ${event.data}, Error: ${error}`);
6553
+ return;
6554
+ }
6555
+ const frames = Array.isArray(parsed) ? parsed : [parsed];
6556
+ frames.forEach((frame) => this.dispatch(frame));
6557
+ this.emit("message", event);
5794
6558
  };
5795
- }
5796
-
5797
- // src/utils/eth.ts
5798
- var eth_exports = {};
5799
- __export(eth_exports, {
5800
- ethRandomPrivateKey: () => ethRandomPrivateKey,
5801
- validateAndParseEthAddress: () => validateAndParseEthAddress
5802
- });
5803
- import { secp256k1 } from "@noble/curves/secp256k1";
5804
- function ethRandomPrivateKey() {
5805
- return sanitizeHex(buf2hex(secp256k1.utils.randomPrivateKey()));
5806
- }
5807
- function validateAndParseEthAddress(address) {
5808
- assertInRange(address, ZERO, 2n ** 160n - 1n, "Ethereum Address ");
5809
- const result = addHexPrefix(removeHexPrefix(toHex(address)).padStart(40, "0"));
5810
- assert(Boolean(result.match(/^(0x)?[0-9a-f]{40}$/)), "Invalid Ethereum Address Format");
5811
- return result;
5812
- }
5813
-
5814
- // src/utils/connect/fetch.ts
5815
- var fetch_default = typeof globalThis !== "undefined" && typeof globalThis.fetch !== "undefined" && globalThis.fetch.bind(globalThis) || typeof window !== "undefined" && typeof window.fetch !== "undefined" && window.fetch.bind(window) || typeof global !== "undefined" && typeof global.fetch !== "undefined" && global.fetch.bind(global) || (() => {
5816
- throw new LibraryError(
5817
- "'fetch()' not detected, use the 'baseFetch' constructor parameter to set it"
5818
- );
5819
- });
5820
-
5821
- // src/utils/provider.ts
5822
- var provider_exports = {};
5823
- __export(provider_exports, {
5824
- Block: () => Block,
5825
- createSierraContractClass: () => createSierraContractClass,
5826
- extractAbi: () => extractAbi,
5827
- getDefaultNodeUrl: () => getDefaultNodeUrl,
5828
- getDefaultNodes: () => getDefaultNodes,
5829
- getSupportedRpcVersions: () => getSupportedRpcVersions,
5830
- parseContract: () => parseContract,
5831
- validBlockTags: () => validBlockTags,
5832
- wait: () => wait
5833
- });
5834
- function wait(delay) {
5835
- return new Promise((res) => {
5836
- setTimeout(res, delay);
5837
- });
5838
- }
5839
- async function createSierraContractClass(contract) {
5840
- const result = { ...contract };
5841
- delete result.sierra_program_debug_info;
5842
- result.abi = formatSpaces(stringify2(contract.abi));
5843
- result.sierra_program = formatSpaces(stringify2(contract.sierra_program));
5844
- result.sierra_program = await compressProgram(result.sierra_program);
5845
- return result;
5846
- }
5847
- async function parseContract(contract) {
5848
- const parsedContract = isString(contract) ? parse2(contract) : contract;
5849
- if (!isSierra(contract)) {
5850
- return {
5851
- ...parsedContract,
5852
- ..."program" in parsedContract && {
5853
- program: await compressProgram(parsedContract.program)
6559
+ /** Routes one inbound frame: a reply to the request waiting for it, or a notification. */
6560
+ dispatch(frame) {
6561
+ if (isObject(frame) && "id" in frame) {
6562
+ const waiter = this.pending.get(frame.id);
6563
+ if (!waiter) {
6564
+ if ("error" in frame) {
6565
+ logger.error(
6566
+ `WsTransport: node rejected a frame outright: ${stringify2(frame.error)}`
6567
+ );
6568
+ return;
6569
+ }
6570
+ logger.warn(`WsTransport: reply for unknown request id ${frame.id}, dropped.`);
6571
+ return;
5854
6572
  }
5855
- };
6573
+ this.pending.delete(frame.id);
6574
+ clearTimeout(waiter.timer);
6575
+ waiter.resolve(frame);
6576
+ return;
6577
+ }
6578
+ if (isObject(frame) && "method" in frame) {
6579
+ this.emit("notification", frame);
6580
+ return;
6581
+ }
6582
+ logger.warn("WsTransport: frame is neither a reply nor a notification, dropped.");
6583
+ }
6584
+ handleError = (event) => {
6585
+ logger.warn(`WsTransport: socket error on ${this.nodeUrl}: ${event.type}`);
6586
+ this.onError(event);
6587
+ this.emit("error", event);
6588
+ };
6589
+ /**
6590
+ * What happens on a socket error. This class only logs; a reconnecting subclass treats it as a
6591
+ * failed attempt, because a refused connection may produce an `error` with no `close` behind it.
6592
+ */
6593
+ onError(_event) {
6594
+ }
6595
+ setState(next) {
6596
+ if (this.state === next) return;
6597
+ this.state = next;
6598
+ this.emit("statechange", void 0);
6599
+ }
6600
+ emit(event, payload) {
6601
+ this.listeners[event].forEach((listener) => listener(payload));
6602
+ }
6603
+ /** The current connection state. The `getSnapshot` half of the `useSyncExternalStore` pair. */
6604
+ getState() {
6605
+ return this.state;
6606
+ }
6607
+ /** Whether the socket is open right now. */
6608
+ isConnected() {
6609
+ return this.socket.readyState === ws_default.OPEN;
5856
6610
  }
5857
- return await createSierraContractClass(parsedContract);
5858
- }
5859
- function extractAbi(contract) {
5860
- return isString(contract.abi) ? parse2(contract.abi) : contract.abi;
5861
- }
5862
- var getDefaultNodeUrl = (networkName, rpcVersion) => {
5863
- logger.info("Using default public node url, please provide nodeUrl in provider options!");
5864
- const rpcNodes = getDefaultNodes(rpcVersion ?? config.get("rpcVersion"));
5865
- const nodes = rpcNodes[networkName ?? _NetworkName.SN_SEPOLIA];
5866
- const randIdx = Math.floor(Math.random() * nodes.length);
5867
- return nodes[randIdx];
5868
- };
5869
- function getDefaultNodes(rpcVersion) {
5870
- const apiVersion = toApiVersion(rpcVersion);
5871
- return Object.fromEntries(
5872
- Object.entries(RPC_DEFAULT_NODES).map(([key, urls]) => [
5873
- key,
5874
- urls.map((url) => `${url}${apiVersion}`)
5875
- ])
5876
- );
5877
- }
5878
- function getSupportedRpcVersions() {
5879
- return [...new Set(Object.values(_SupportedRpcVersion))];
5880
- }
5881
- var validBlockTags = Object.values(BlockTag);
5882
- var Block = class {
5883
6611
  /**
5884
- * @param {BlockIdentifier} hash if not null, contains the block hash
6612
+ * Allocates the next wire id, starting at 0.
6613
+ *
6614
+ * Exposed because a caller may write to the socket itself rather than through `request` —
6615
+ * `WebSocketChannel.send()` is fire-and-forget and correlates nothing. Drawing from this same
6616
+ * sequence is what stops such a write from colliding with a request this transport is
6617
+ * tracking, which would hand one caller the other's reply.
5885
6618
  */
5886
- hash = null;
6619
+ allocateRequestId() {
6620
+ const id = this.wireId;
6621
+ this.wireId += 1;
6622
+ return id;
6623
+ }
5887
6624
  /**
5888
- * @param {BlockIdentifier} number if not null, contains the block number
6625
+ * Subscribes to one of the transport's two streams.
6626
+ *
6627
+ * `'statechange'` fires on every connection state transition; `'notification'` fires for
6628
+ * every frame the node pushes on its own.
6629
+ *
6630
+ * @returns the unsubscribe function, which is what `useSyncExternalStore` requires of its
6631
+ * `subscribe` argument.
5889
6632
  */
5890
- number = null;
6633
+ on(event, listener) {
6634
+ const listeners = this.listeners[event];
6635
+ listeners.add(listener);
6636
+ return () => {
6637
+ listeners.delete(listener);
6638
+ };
6639
+ }
5891
6640
  /**
5892
- * @param {BlockIdentifier} tag if not null, contains "pre_confirmed" or "latest"
6641
+ * Waits for the socket to be usable.
6642
+ *
6643
+ * A refused connection surfaces as `error`, as `close`, or as both: a browser emits `error`
6644
+ * then `close`, while a gateway turning the connection away may only close it. Settling on
6645
+ * whichever arrives first is what keeps that last case from hanging until `requestTimeout`.
6646
+ *
6647
+ * Registered as listeners rather than assigned to `onopen` / `onerror`, so nothing the
6648
+ * socket's owner installed gets clobbered.
5893
6649
  */
5894
- tag = null;
5895
- setIdentifier(__identifier) {
5896
- if (isString(__identifier)) {
5897
- if (isDecimalString2(__identifier)) {
5898
- this.number = parseInt(__identifier, 10);
5899
- } else if (isHex(__identifier)) {
5900
- this.hash = __identifier;
5901
- } else if (validBlockTags.includes(__identifier)) {
5902
- this.tag = __identifier;
5903
- } else {
5904
- throw TypeError(`Block identifier unmanaged: ${__identifier}`);
6650
+ ensureReady() {
6651
+ this.ready ??= new Promise((resolve, reject) => {
6652
+ if (this.socket.readyState === ws_default.OPEN) {
6653
+ resolve();
6654
+ return;
5905
6655
  }
5906
- } else if (isBigInt(__identifier)) {
5907
- this.hash = toHex(__identifier);
5908
- } else if (isNumber(__identifier)) {
5909
- this.number = __identifier;
5910
- } else {
5911
- this.tag = BlockTag.LATEST;
5912
- }
5913
- if (isNumber(this.number) && this.number < 0) {
5914
- throw TypeError(`Block number (${this.number}) can't be negative`);
6656
+ if (this.socket.readyState === ws_default.CLOSING || this.socket.readyState === ws_default.CLOSED) {
6657
+ reject(
6658
+ new WebSocketNotConnectedError(
6659
+ `WebSocket connection to ${this.nodeUrl} is already closed`
6660
+ )
6661
+ );
6662
+ return;
6663
+ }
6664
+ const detach = () => {
6665
+ this.socket.removeEventListener("open", onOpen);
6666
+ this.socket.removeEventListener("error", onFailure);
6667
+ this.socket.removeEventListener("close", onFailure);
6668
+ };
6669
+ const onOpen = () => {
6670
+ detach();
6671
+ resolve();
6672
+ };
6673
+ const onFailure = () => {
6674
+ detach();
6675
+ reject(
6676
+ new WebSocketNotConnectedError(
6677
+ `WebSocket connection to ${this.nodeUrl} was not established`
6678
+ )
6679
+ );
6680
+ };
6681
+ this.socket.addEventListener("open", onOpen);
6682
+ this.socket.addEventListener("error", onFailure);
6683
+ this.socket.addEventListener("close", onFailure);
6684
+ });
6685
+ return this.ready;
6686
+ }
6687
+ async request(body) {
6688
+ if (!this.isConnected()) await this.ensureReady();
6689
+ if (!this.isConnected()) {
6690
+ throw new WebSocketNotConnectedError(`WebSocket to ${this.nodeUrl} is not open`);
6691
+ }
6692
+ const isBatch = Array.isArray(body);
6693
+ const entries = isBatch ? body : [body];
6694
+ const callerIds = /* @__PURE__ */ new Map();
6695
+ const wireEntries = entries.map((entry) => {
6696
+ const wireId = this.allocateRequestId();
6697
+ callerIds.set(wireId, entry.id);
6698
+ return { ...entry, id: wireId };
6699
+ });
6700
+ const replies = wireEntries.map((entry) => this.expect(entry.id, entry.method));
6701
+ try {
6702
+ this.socket.send(stringify2(isBatch ? wireEntries : wireEntries[0]));
6703
+ } catch (error) {
6704
+ wireEntries.forEach(
6705
+ (entry) => this.settle(
6706
+ entry.id,
6707
+ new WebSocketNotConnectedError(`Request ${entry.method} could not be sent: ${error}`)
6708
+ )
6709
+ );
5915
6710
  }
6711
+ const settled = await Promise.all(replies);
6712
+ const restored = settled.map(
6713
+ (reply) => ({ ...reply, id: callerIds.get(reply.id) })
6714
+ );
6715
+ return isBatch ? restored : restored[0];
6716
+ }
6717
+ /** Registers a slot for one wire id and hands back the promise that settles when answered. */
6718
+ expect(id, method) {
6719
+ return new Promise((resolve, reject) => {
6720
+ const timer = setTimeout(() => {
6721
+ this.pending.delete(id);
6722
+ reject(
6723
+ new TimeoutError(`Request ${method} (id: ${id}) timed out after ${this.requestTimeout}ms`)
6724
+ );
6725
+ }, this.requestTimeout);
6726
+ this.pending.set(id, { method, resolve, reject, timer });
6727
+ });
6728
+ }
6729
+ /** Fails one pending request by wire id. */
6730
+ settle(id, error) {
6731
+ const waiter = this.pending.get(id);
6732
+ if (!waiter) return;
6733
+ this.pending.delete(id);
6734
+ clearTimeout(waiter.timer);
6735
+ waiter.reject(error);
5916
6736
  }
5917
6737
  /**
5918
- * Create a Block instance
5919
- * @param {BlockIdentifier} _identifier hex string and BigInt are detected as block hashes.
5920
- * decimal string and number are detected as block numbers.
5921
- * text string are detected as block tag.
5922
- * null is considered as a 'latest' block tag.
6738
+ * Fails every request still on the wire.
6739
+ *
6740
+ * Their only other exit is the `requestTimeout` timer, so without this the caller waits the
6741
+ * whole timeout — 60s by default — for a reply that can no longer arrive, and that pending
6742
+ * timer keeps the Node event loop alive for just as long.
6743
+ */
6744
+ rejectPending(reason) {
6745
+ if (this.pending.size === 0) return;
6746
+ const waiting = Array.from(this.pending.entries());
6747
+ this.pending.clear();
6748
+ logger.info(
6749
+ `WsTransport: rejecting ${waiting.length} in-flight request(s). Reason: ${reason}.`
6750
+ );
6751
+ waiting.forEach(([id, { method, reject, timer }]) => {
6752
+ clearTimeout(timer);
6753
+ reject(
6754
+ new WebSocketNotConnectedError(`Request ${method} (id: ${id}) went unanswered: ${reason}`)
6755
+ );
6756
+ });
6757
+ }
6758
+ /**
6759
+ * Closes the socket.
6760
+ *
6761
+ * One object owns the socket and one object closes it. Dropping every reference to this
6762
+ * transport does not close anything: an open socket is a libuv handle the Node event loop
6763
+ * holds onto, so it must be closed explicitly.
5923
6764
  */
5924
- constructor(_identifier) {
5925
- this.setIdentifier(_identifier);
6765
+ close(code, reason) {
6766
+ this.rejectPending("the connection was closed by the user");
6767
+ this.setState("closed");
6768
+ this.socket.close(code, reason);
5926
6769
  }
5927
- // TODO: fix any
6770
+ };
6771
+
6772
+ // src/channel/transport/reconnectingWs.ts
6773
+ var ReconnectingWsTransport = class extends WsTransport {
6774
+ autoReconnect;
6775
+ reconnectOptions;
6776
+ isReconnecting = false;
6777
+ reconnectAttempts = 0;
6778
+ userInitiatedClose = false;
6779
+ reconnectTimeoutId = null;
5928
6780
  /**
5929
- * @returns {any} the identifier as a string
5930
- * @example
5931
- * ```typescript
5932
- * const result = new provider.Block(123456n).queryIdentifier;
5933
- * // result = "blockHash=0x1e240"
5934
- * ```
6781
+ * Fires once a connection has stayed open long enough to be considered stable, resetting the
6782
+ * attempt counter. A gateway that accepts then immediately drops never reaches it, so its
6783
+ * attempts keep accumulating toward the cap instead of resetting every cycle.
5935
6784
  */
5936
- get queryIdentifier() {
5937
- if (this.number !== null) {
5938
- return `blockNumber=${this.number}`;
6785
+ reconnectStabilityTimeoutId = null;
6786
+ /**
6787
+ * Requests made while no usable connection existed. They carry no timeout of their own — the
6788
+ * base class only arms one when a request reaches the wire — so the only ways out are being
6789
+ * sent after a reconnection, or being rejected here.
6790
+ */
6791
+ requestQueue = [];
6792
+ /** Guards against one failed attempt arming two retries when both `error` and `close` arrive. */
6793
+ attemptSettled = false;
6794
+ reconnectedHooks = /* @__PURE__ */ new Set();
6795
+ constructor(options) {
6796
+ super(options);
6797
+ this.autoReconnect = options.autoReconnect ?? true;
6798
+ this.reconnectOptions = {
6799
+ retries: options.reconnectOptions?.retries ?? 5,
6800
+ delay: options.reconnectOptions?.delay ?? 2e3,
6801
+ exponential: options.reconnectOptions?.exponential ?? true,
6802
+ stableConnectionThreshold: options.reconnectOptions?.stableConnectionThreshold ?? 5e3
6803
+ };
6804
+ }
6805
+ /**
6806
+ * Whether a request made right now would be held for a reconnection rather than sent.
6807
+ *
6808
+ * Exposed so the `WebSocketChannel` façade can reproduce the refusal its `send()` used to
6809
+ * raise: it refused only when nothing was going to pick the request up later.
6810
+ */
6811
+ get queuesRequests() {
6812
+ return this.isReconnecting || !this.isConnected() && this.autoReconnect && !this.userInitiatedClose;
6813
+ }
6814
+ /**
6815
+ * Registers work to run after a reconnection opens, before the request queue is flushed.
6816
+ * @returns the unregister function.
6817
+ */
6818
+ onReconnected(hook) {
6819
+ this.reconnectedHooks.add(hook);
6820
+ return () => {
6821
+ this.reconnectedHooks.delete(hook);
6822
+ };
6823
+ }
6824
+ onOpened() {
6825
+ this.attemptSettled = true;
6826
+ const wasReconnecting = this.isReconnecting;
6827
+ this.isReconnecting = false;
6828
+ this.scheduleReconnectAttemptsReset();
6829
+ super.onOpened();
6830
+ if (wasReconnecting) {
6831
+ logger.info("WebSocket: Reconnection successful.");
6832
+ this.completeReconnection();
6833
+ return;
5939
6834
  }
5940
- if (this.hash !== null) {
5941
- return `blockHash=${this.hash}`;
6835
+ this.processRequestQueue();
6836
+ }
6837
+ onClosed() {
6838
+ if (this.userInitiatedClose || !this.autoReconnect) {
6839
+ this.setState("closed");
6840
+ return;
5942
6841
  }
5943
- return `blockNumber=${this.tag}`;
6842
+ if (this.isReconnecting) {
6843
+ this.scheduleRetry();
6844
+ return;
6845
+ }
6846
+ this.startReconnect();
6847
+ }
6848
+ onError() {
6849
+ if (this.isConnected()) return;
6850
+ if (this.isReconnecting) this.scheduleRetry();
6851
+ }
6852
+ startReconnect() {
6853
+ if (this.isReconnecting || !this.autoReconnect) return;
6854
+ this.clearStabilityTimer();
6855
+ this.isReconnecting = true;
6856
+ this.setState("reconnecting");
6857
+ this.tryReconnect();
6858
+ }
6859
+ tryReconnect = () => {
6860
+ if (this.reconnectAttempts >= this.reconnectOptions.retries) {
6861
+ logger.error("WebSocket: Maximum reconnection retries reached. Giving up.");
6862
+ this.isReconnecting = false;
6863
+ this.setState("closed");
6864
+ this.rejectRequestQueue(
6865
+ `reconnection gave up after ${this.reconnectOptions.retries} attempts`
6866
+ );
6867
+ return;
6868
+ }
6869
+ this.reconnectAttempts += 1;
6870
+ logger.info(
6871
+ `WebSocket: Connection lost. Attempting to reconnect... (${this.reconnectAttempts}/${this.reconnectOptions.retries})`
6872
+ );
6873
+ this.attemptSettled = false;
6874
+ this.swapSocket(new this.WsImplementation(this.nodeUrl));
6875
+ };
6876
+ scheduleRetry() {
6877
+ if (this.attemptSettled || !this.isReconnecting) return;
6878
+ this.attemptSettled = true;
6879
+ const delay = this.reconnectOptions.exponential ? this.reconnectOptions.delay * 2 ** (this.reconnectAttempts - 1) : this.reconnectOptions.delay;
6880
+ logger.info(`WebSocket: Reconnect attempt failed. Retrying in ${delay}ms.`);
6881
+ this.reconnectTimeoutId = setTimeout(this.tryReconnect, delay);
6882
+ }
6883
+ scheduleReconnectAttemptsReset() {
6884
+ this.clearStabilityTimer();
6885
+ this.reconnectStabilityTimeoutId = setTimeout(() => {
6886
+ this.reconnectAttempts = 0;
6887
+ this.reconnectStabilityTimeoutId = null;
6888
+ }, this.reconnectOptions.stableConnectionThreshold);
6889
+ }
6890
+ clearStabilityTimer() {
6891
+ if (!this.reconnectStabilityTimeoutId) return;
6892
+ clearTimeout(this.reconnectStabilityTimeoutId);
6893
+ this.reconnectStabilityTimeoutId = null;
6894
+ }
6895
+ completeReconnection() {
6896
+ Promise.all(Array.from(this.reconnectedHooks).map((hook) => hook())).catch((error) => {
6897
+ logger.error(`WebSocket: a reconnection hook failed: ${error}`);
6898
+ }).then(() => this.processRequestQueue());
6899
+ }
6900
+ async request(body) {
6901
+ if (this.isReconnecting || !this.isConnected() && this.autoReconnect && !this.userInitiatedClose) {
6902
+ logger.info("WebSocket: Connection unavailable, queueing request.");
6903
+ return new Promise((resolve, reject) => {
6904
+ this.requestQueue.push({ body, resolve, reject });
6905
+ });
6906
+ }
6907
+ return super.request(body);
6908
+ }
6909
+ processRequestQueue() {
6910
+ const pending = this.requestQueue;
6911
+ this.requestQueue = [];
6912
+ logger.info(`WebSocket: Processing ${pending.length} queued requests.`);
6913
+ pending.forEach(({ body, resolve, reject }) => {
6914
+ this.request(body).then(resolve).catch(reject);
6915
+ });
6916
+ }
6917
+ rejectRequestQueue(reason) {
6918
+ if (this.requestQueue.length === 0) return;
6919
+ const pending = this.requestQueue;
6920
+ this.requestQueue = [];
6921
+ logger.info(`WebSocket: Rejecting ${pending.length} queued request(s). Reason: ${reason}.`);
6922
+ pending.forEach(({ body, reject }) => {
6923
+ const method = Array.isArray(body) ? "batch" : body.method;
6924
+ reject(new WebSocketNotConnectedError(`Request ${method} was never sent: ${reason}`));
6925
+ });
5944
6926
  }
5945
- // TODO: fix any
5946
6927
  /**
5947
- * @returns {any} the identifier as an object
5948
- * @example
5949
- * ```typescript
5950
- * const result = new provider.Block(56789).identifier;
5951
- * // result = { block_number: 56789 }
5952
- * ```
6928
+ * Manually opens a fresh connection, cancelling the effect of an earlier `close()`.
6929
+ *
6930
+ * Flagged as a reconnection so `onOpened` runs `completeReconnection()`: a manual reconnect owes
6931
+ * the caller exactly what an automatic one delivers — subscriptions restored first, then the
6932
+ * request queue flushed behind them. Without the flag the socket came back empty, which is the
6933
+ * React StrictMode path: mount, cleanup `close()`, remount `reconnect()`, subscriptions
6934
+ * silently lost.
6935
+ *
6936
+ * Reporting `'reconnecting'` rather than `'connecting'` is the honest label, and it keeps
6937
+ * `queuesRequests` true for the gap, so a request issued before the socket opens is queued
6938
+ * instead of refused.
5953
6939
  */
5954
- get identifier() {
5955
- if (this.number !== null) {
5956
- return { block_number: this.number };
6940
+ reconnect() {
6941
+ this.userInitiatedClose = false;
6942
+ this.attemptSettled = false;
6943
+ this.isReconnecting = true;
6944
+ this.setState("reconnecting");
6945
+ this.swapSocket(new this.WsImplementation(this.nodeUrl));
6946
+ }
6947
+ /**
6948
+ * Closes the socket for good, and stops the automatic reconnection with it.
6949
+ *
6950
+ * Any request still queued is rejected rather than left waiting for a connection that is no
6951
+ * longer coming. Call {@link reconnect} to start over afterwards.
6952
+ * @param code - WebSocket close code sent to the node.
6953
+ * @param reason - Human-readable close reason sent to the node.
6954
+ */
6955
+ close(code, reason) {
6956
+ if (this.reconnectTimeoutId) {
6957
+ clearTimeout(this.reconnectTimeoutId);
6958
+ this.reconnectTimeoutId = null;
5957
6959
  }
5958
- if (this.hash !== null) {
5959
- return { block_hash: this.hash };
6960
+ this.clearStabilityTimer();
6961
+ this.userInitiatedClose = true;
6962
+ this.isReconnecting = false;
6963
+ this.rejectRequestQueue("the connection was closed by the user");
6964
+ super.close(code, reason);
6965
+ }
6966
+ };
6967
+
6968
+ // src/utils/batch/index.ts
6969
+ var BatchClient = class {
6970
+ nodeUrl;
6971
+ headers;
6972
+ interval;
6973
+ requestId = 0;
6974
+ pendingRequests = {};
6975
+ batchPromises = {};
6976
+ delayTimer;
6977
+ delayPromise;
6978
+ delayPromiseResolve;
6979
+ transport;
6980
+ rpcMethods;
6981
+ constructor(options) {
6982
+ this.nodeUrl = options.nodeUrl;
6983
+ this.headers = options.headers;
6984
+ this.interval = options.interval;
6985
+ this.transport = options.transport ?? new HttpTransport({
6986
+ nodeUrl: options.nodeUrl,
6987
+ headers: options.headers,
6988
+ baseFetch: options.baseFetch
6989
+ });
6990
+ this.rpcMethods = options.rpcMethods;
6991
+ }
6992
+ async wait() {
6993
+ if (!this.delayPromise || !this.delayPromiseResolve) {
6994
+ this.delayPromise = new Promise((resolve) => {
6995
+ this.delayPromiseResolve = resolve;
6996
+ });
5960
6997
  }
5961
- return this.tag;
6998
+ if (this.delayTimer) {
6999
+ clearTimeout(this.delayTimer);
7000
+ this.delayTimer = void 0;
7001
+ }
7002
+ this.delayTimer = setTimeout(() => {
7003
+ if (this.delayPromiseResolve) {
7004
+ this.delayPromiseResolve();
7005
+ this.delayPromise = void 0;
7006
+ this.delayPromiseResolve = void 0;
7007
+ }
7008
+ }, this.interval);
7009
+ return this.delayPromise;
7010
+ }
7011
+ addPendingRequest(method, params, id) {
7012
+ const request = {
7013
+ id: id ?? `batched_${this.requestId += 1}`,
7014
+ jsonrpc: "2.0",
7015
+ method: method.description || String(method),
7016
+ params: params ?? void 0
7017
+ };
7018
+ this.pendingRequests[request.id] = request;
7019
+ return request.id;
7020
+ }
7021
+ async sendBatch(requests) {
7022
+ return this.transport.request(requests);
5962
7023
  }
5963
7024
  /**
5964
- * change the identifier of an existing Block instance
5965
- * @example
5966
- * ```typescript
5967
- * const myBlock = new provider.Block("latest");
5968
- * myBlock.identifier ="0x3456789abc";
5969
- * const result = myBlock.identifier;
5970
- * // result = { block_hash: '0x3456789abc' }
5971
- * ```
7025
+ * Automatically batches and fetches JSON-RPC calls in a single request.
7026
+ * @param method Method to call
7027
+ * @param params Method parameters
7028
+ * @param id JSON-RPC Request ID
7029
+ * @returns JSON-RPC Response
5972
7030
  */
5973
- set identifier(_identifier) {
5974
- this.setIdentifier(_identifier);
7031
+ async fetch(method, params, id) {
7032
+ const requestId = this.addPendingRequest(method, params, id);
7033
+ await this.wait();
7034
+ const requests = this.pendingRequests;
7035
+ this.pendingRequests = {};
7036
+ if (!this.batchPromises[requestId]) {
7037
+ const promise = this.sendBatch(Object.values(requests));
7038
+ Object.keys(requests).forEach((key) => {
7039
+ this.batchPromises[key] = promise;
7040
+ });
7041
+ }
7042
+ const results = await this.batchPromises[requestId];
7043
+ delete this.batchPromises[requestId];
7044
+ const result = results.find((res) => res.id === requestId);
7045
+ if (!result)
7046
+ throw new Error(`Couldn't find the result for the request. Method: ${String(method)}`);
7047
+ return result;
5975
7048
  }
5976
- valueOf = () => this.number;
5977
- toString = () => this.hash;
5978
7049
  };
5979
7050
 
7051
+ // src/utils/eth.ts
7052
+ var eth_exports = {};
7053
+ __export(eth_exports, {
7054
+ ethRandomPrivateKey: () => ethRandomPrivateKey,
7055
+ validateAndParseEthAddress: () => validateAndParseEthAddress
7056
+ });
7057
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
7058
+ function ethRandomPrivateKey() {
7059
+ return sanitizeHex(buf2hex(secp256k1.utils.randomSecretKey()));
7060
+ }
7061
+ function validateAndParseEthAddress(address) {
7062
+ assertInRange(address, ZERO, 2n ** 160n - 1n, "Ethereum Address ");
7063
+ const result = addHexPrefix(removeHexPrefix(toHex(address)).padStart(40, "0"));
7064
+ assert(Boolean(result.match(/^(0x)?[0-9a-f]{40}$/)), "Invalid Ethereum Address Format");
7065
+ return result;
7066
+ }
7067
+
7068
+ // src/utils/connect/fetch.ts
7069
+ var fetch_default = typeof globalThis !== "undefined" && typeof globalThis.fetch !== "undefined" && globalThis.fetch.bind(globalThis) || typeof window !== "undefined" && typeof window.fetch !== "undefined" && window.fetch.bind(window) || typeof global !== "undefined" && typeof global.fetch !== "undefined" && global.fetch.bind(global) || (() => {
7070
+ throw new LibraryError(
7071
+ "'fetch()' not detected, use the 'baseFetch' constructor parameter to set it"
7072
+ );
7073
+ });
7074
+
5980
7075
  // src/utils/transaction/index.ts
5981
7076
  var transaction_exports = {};
5982
7077
  __export(transaction_exports, {
@@ -6064,6 +7159,8 @@ var RpcChannel = class {
6064
7159
  transactionRetryIntervalFallback;
6065
7160
  batchClient;
6066
7161
  baseFetch;
7162
+ /** Carries envelopes to the node. `HttpTransport` unless the caller injected its own. */
7163
+ transport;
6067
7164
  constructor(optionsOrProvider) {
6068
7165
  const {
6069
7166
  baseFetch,
@@ -6075,6 +7172,7 @@ var RpcChannel = class {
6075
7172
  retries,
6076
7173
  specVersion,
6077
7174
  transactionRetryIntervalFallback,
7175
+ transport,
6078
7176
  waitMode
6079
7177
  } = optionsOrProvider || {};
6080
7178
  if (Object.values(_NetworkName).includes(nodeUrl)) {
@@ -6093,6 +7191,11 @@ var RpcChannel = class {
6093
7191
  this.specVersion = specVersion;
6094
7192
  this.transactionRetryIntervalFallback = transactionRetryIntervalFallback ?? channelDefaults.options.transactionRetryIntervalFallback;
6095
7193
  this.waitMode = waitMode ?? false;
7194
+ this.transport = transport ?? new HttpTransport({
7195
+ nodeUrl: this.nodeUrl,
7196
+ headers: this.headers,
7197
+ baseFetch: this.baseFetch
7198
+ });
6096
7199
  this.requestId = 0;
6097
7200
  if (isNumber(batch)) {
6098
7201
  this.batchClient = new BatchClient({
@@ -6100,6 +7203,7 @@ var RpcChannel = class {
6100
7203
  headers: this.headers,
6101
7204
  interval: batch,
6102
7205
  baseFetch: this.baseFetch,
7206
+ transport: this.transport,
6103
7207
  rpcMethods: {}
6104
7208
  // Type information only, not used at runtime
6105
7209
  });
@@ -6115,6 +7219,12 @@ var RpcChannel = class {
6115
7219
  setChainId(chainId) {
6116
7220
  this.chainId = chainId;
6117
7221
  }
7222
+ /**
7223
+ * Escape hatch: POSTs a single JSON-RPC call and hands back the raw HTTP response.
7224
+ *
7225
+ * No longer on the `fetchEndpoint` path — that one goes through the transport — but kept
7226
+ * because it is public API and `RpcProvider.fetch()` is built on it.
7227
+ */
6118
7228
  fetch(method, params, id = 0) {
6119
7229
  const rpcRequestBody = {
6120
7230
  id,
@@ -6122,11 +7232,10 @@ var RpcChannel = class {
6122
7232
  method,
6123
7233
  ...params && { params }
6124
7234
  };
6125
- return this.baseFetch(this.nodeUrl, {
6126
- method: "POST",
6127
- body: stringify2(rpcRequestBody),
6128
- headers: this.headers
6129
- });
7235
+ return postJsonRpc(
7236
+ { nodeUrl: this.nodeUrl, headers: this.headers, baseFetch: this.baseFetch },
7237
+ rpcRequestBody
7238
+ );
6130
7239
  }
6131
7240
  errorHandler(method, params, rpcError, otherError) {
6132
7241
  if (rpcError) {
@@ -6143,11 +7252,18 @@ var RpcChannel = class {
6143
7252
  try {
6144
7253
  let error;
6145
7254
  let result;
7255
+ this.requestId += 1;
7256
+ const id = this.requestId;
6146
7257
  if (this.batchClient) {
6147
- ({ error, result } = await this.batchClient.fetch(method, params, this.requestId += 1));
7258
+ ({ error, result } = await this.batchClient.fetch(method, params, id));
6148
7259
  } else {
6149
- const rawResult = await this.fetch(method, params, this.requestId += 1);
6150
- ({ error, result } = await rawResult.json());
7260
+ const response = await this.transport.request({
7261
+ id,
7262
+ jsonrpc: "2.0",
7263
+ method,
7264
+ ...params && { params }
7265
+ });
7266
+ ({ error, result } = response);
6151
7267
  }
6152
7268
  this.errorHandler(method, params, error);
6153
7269
  if (result === void 0) {
@@ -6598,11 +7714,20 @@ var RpcChannel = class {
6598
7714
  }
6599
7715
  };
6600
7716
 
6601
- // src/channel/rpc_0_10_2.ts
6602
- var rpc_0_10_2_exports = {};
6603
- __export(rpc_0_10_2_exports, {
6604
- RpcChannel: () => RpcChannel2
7717
+ // src/channel/v0_9/index.ts
7718
+ var SubscriptionChannel2 = class extends SubscriptionChannel {
7719
+ id = "RPC0.9.0-subscriptions";
7720
+ channelSpecVersion = _SupportedRpcVersion.v0_9_0;
7721
+ };
7722
+
7723
+ // src/channel/v0_10_2/index.ts
7724
+ var v0_10_2_exports = {};
7725
+ __export(v0_10_2_exports, {
7726
+ RpcChannel: () => RpcChannel2,
7727
+ SubscriptionChannel: () => SubscriptionChannel
6605
7728
  });
7729
+
7730
+ // src/channel/rpc_0_10_2.ts
6606
7731
  import * as RPC from "@starknet-io/starknet-types-0101";
6607
7732
  var RpcChannel2 = class {
6608
7733
  id = "RPC0.10.2";
@@ -6625,6 +7750,8 @@ var RpcChannel2 = class {
6625
7750
  transactionRetryIntervalFallback;
6626
7751
  batchClient;
6627
7752
  baseFetch;
7753
+ /** Carries envelopes to the node. `HttpTransport` unless the caller injected its own. */
7754
+ transport;
6628
7755
  constructor(optionsOrProvider) {
6629
7756
  const {
6630
7757
  baseFetch,
@@ -6636,6 +7763,7 @@ var RpcChannel2 = class {
6636
7763
  retries,
6637
7764
  specVersion,
6638
7765
  transactionRetryIntervalFallback,
7766
+ transport,
6639
7767
  waitMode
6640
7768
  } = optionsOrProvider || {};
6641
7769
  if (Object.values(_NetworkName).includes(nodeUrl)) {
@@ -6654,6 +7782,11 @@ var RpcChannel2 = class {
6654
7782
  this.specVersion = specVersion;
6655
7783
  this.transactionRetryIntervalFallback = transactionRetryIntervalFallback ?? channelDefaults.options.transactionRetryIntervalFallback;
6656
7784
  this.waitMode = waitMode ?? false;
7785
+ this.transport = transport ?? new HttpTransport({
7786
+ nodeUrl: this.nodeUrl,
7787
+ headers: this.headers,
7788
+ baseFetch: this.baseFetch
7789
+ });
6657
7790
  this.requestId = 0;
6658
7791
  if (isNumber(batch)) {
6659
7792
  this.batchClient = new BatchClient({
@@ -6661,6 +7794,7 @@ var RpcChannel2 = class {
6661
7794
  headers: this.headers,
6662
7795
  interval: batch,
6663
7796
  baseFetch: this.baseFetch,
7797
+ transport: this.transport,
6664
7798
  rpcMethods: {}
6665
7799
  // Type information only, not used at runtime
6666
7800
  });
@@ -6676,6 +7810,12 @@ var RpcChannel2 = class {
6676
7810
  setChainId(chainId) {
6677
7811
  this.chainId = chainId;
6678
7812
  }
7813
+ /**
7814
+ * Escape hatch: POSTs a single JSON-RPC call and hands back the raw HTTP response.
7815
+ *
7816
+ * No longer on the `fetchEndpoint` path — that one goes through the transport — but kept
7817
+ * because it is public API and `RpcProvider.fetch()` is built on it.
7818
+ */
6679
7819
  fetch(method, params, id = 0) {
6680
7820
  const rpcRequestBody = {
6681
7821
  id,
@@ -6683,11 +7823,10 @@ var RpcChannel2 = class {
6683
7823
  method,
6684
7824
  ...params && { params }
6685
7825
  };
6686
- return this.baseFetch(this.nodeUrl, {
6687
- method: "POST",
6688
- body: stringify2(rpcRequestBody),
6689
- headers: this.headers
6690
- });
7826
+ return postJsonRpc(
7827
+ { nodeUrl: this.nodeUrl, headers: this.headers, baseFetch: this.baseFetch },
7828
+ rpcRequestBody
7829
+ );
6691
7830
  }
6692
7831
  errorHandler(method, params, rpcError, otherError) {
6693
7832
  if (rpcError) {
@@ -6704,11 +7843,18 @@ var RpcChannel2 = class {
6704
7843
  try {
6705
7844
  let error;
6706
7845
  let result;
7846
+ this.requestId += 1;
7847
+ const id = this.requestId;
6707
7848
  if (this.batchClient) {
6708
- ({ error, result } = await this.batchClient.fetch(method, params, this.requestId += 1));
7849
+ ({ error, result } = await this.batchClient.fetch(method, params, id));
6709
7850
  } else {
6710
- const rawResult = await this.fetch(method, params, this.requestId += 1);
6711
- ({ error, result } = await rawResult.json());
7851
+ const response = await this.transport.request({
7852
+ id,
7853
+ jsonrpc: "2.0",
7854
+ method,
7855
+ ...params && { params }
7856
+ });
7857
+ ({ error, result } = response);
6712
7858
  }
6713
7859
  this.errorHandler(method, params, error);
6714
7860
  if (result === void 0) {
@@ -7221,176 +8367,23 @@ var RpcChannel2 = class {
7221
8367
  }
7222
8368
  };
7223
8369
 
7224
- // src/channel/rpc_0_10_3.ts
7225
- var rpc_0_10_3_exports = {};
7226
- __export(rpc_0_10_3_exports, {
7227
- RpcChannel: () => RpcChannel3
8370
+ // src/channel/v0_10_3/index.ts
8371
+ var v0_10_3_exports = {};
8372
+ __export(v0_10_3_exports, {
8373
+ RpcChannel: () => RpcChannel3,
8374
+ SubscriptionChannel: () => SubscriptionChannel3
7228
8375
  });
8376
+
8377
+ // src/channel/rpc_0_10_3.ts
7229
8378
  var RpcChannel3 = class extends RpcChannel2 {
7230
8379
  id = "RPC0.10.3";
7231
8380
  channelSpecVersion = _SupportedRpcVersion.v0_10_3;
7232
8381
  };
7233
8382
 
7234
- // src/utils/eventEmitter.ts
7235
- var EventEmitter = class {
7236
- listeners = {};
7237
- on(event, listener) {
7238
- if (!this.listeners[event]) {
7239
- this.listeners[event] = [];
7240
- }
7241
- this.listeners[event].push(listener);
7242
- }
7243
- off(event, listener) {
7244
- if (!this.listeners[event]) {
7245
- return;
7246
- }
7247
- this.listeners[event] = this.listeners[event].filter((l) => l !== listener);
7248
- }
7249
- emit(event, data) {
7250
- if (this.listeners[event]) {
7251
- this.listeners[event].forEach((listener) => listener(data));
7252
- }
7253
- }
7254
- clear() {
7255
- this.listeners = {};
7256
- }
7257
- };
7258
-
7259
- // src/utils/connect/ws.ts
7260
- var ws_default = typeof WebSocket !== "undefined" && WebSocket || typeof globalThis !== "undefined" && globalThis.WebSocket || typeof window !== "undefined" && window.WebSocket && window.WebSocket.bind(window) || typeof global !== "undefined" && global.WebSocket || class {
7261
- constructor() {
7262
- throw new LibraryError(
7263
- "WebSocket module not detected, use the 'websocket' constructor parameter to set a compatible connection"
7264
- );
7265
- }
7266
- };
7267
-
7268
- // src/channel/ws/subscription.ts
7269
- var Subscription = class {
7270
- /**
7271
- * The containing `WebSocketChannel` instance.
7272
- * @internal
7273
- */
7274
- channel;
7275
- /**
7276
- * The JSON-RPC method used to create this subscription.
7277
- * @internal
7278
- */
7279
- method;
7280
- /**
7281
- * The parameters used to create this subscription.
7282
- * @internal
7283
- */
7284
- params;
7285
- /**
7286
- * The unique identifier for this subscription.
7287
- * @internal
7288
- */
7289
- id;
7290
- events = new EventEmitter();
7291
- buffer = [];
7292
- maxBufferSize;
7293
- handler = null;
7294
- _isClosed = false;
7295
- // The unsubscribe request currently on the wire, shared by concurrent callers.
7296
- pendingUnsubscribe = null;
7297
- /**
7298
- * @internal
7299
- * @param options - Subscription configuration options
7300
- */
7301
- constructor(options) {
7302
- this.channel = options.channel;
7303
- this.method = options.method;
7304
- this.params = options.params ?? {};
7305
- this.id = options.id;
7306
- this.maxBufferSize = options.maxBufferSize;
7307
- }
7308
- /**
7309
- * Indicates if the subscription has been closed.
7310
- * @returns {boolean} `true` if unsubscribed, `false` otherwise.
7311
- */
7312
- get isClosed() {
7313
- return this._isClosed;
7314
- }
7315
- /**
7316
- * Closes the subscription locally, without contacting the node.
7317
- *
7318
- * Used when the channel knows the subscription is gone and cannot be recovered — a
7319
- * re-subscribe refused after a reconnection, for instance. Without it the object would keep
7320
- * reporting itself as open while no event could ever reach its handler again.
7321
- * @internal
7322
- */
7323
- _markClosed() {
7324
- if (this._isClosed) return;
7325
- this._isClosed = true;
7326
- this.events.emit("unsubscribe", void 0);
7327
- this.events.clear();
7328
- }
7329
- /**
7330
- * Internal method to handle incoming events from the WebSocket channel.
7331
- * If a handler is attached, it's invoked immediately. Otherwise, the event is buffered.
7332
- * @internal
7333
- * @param {T} data - The event data.
7334
- */
7335
- _handleEvent(data) {
7336
- if (this.handler) {
7337
- this.handler(data);
7338
- } else {
7339
- if (this.buffer.length >= this.maxBufferSize) {
7340
- const droppedEvent = this.buffer.shift();
7341
- logger.warn(`Subscription ${this.id}: Buffer full. Dropping oldest event:`, droppedEvent);
7342
- }
7343
- this.buffer.push(data);
7344
- }
7345
- }
7346
- /**
7347
- * Attaches a handler function to be called for each event.
7348
- *
7349
- * When a handler is attached, any buffered events will be passed to it sequentially.
7350
- * Subsequent events will be passed directly as they arrive.
7351
- *
7352
- * @param {(data: T) => void} handler - The function to call with event data.
7353
- * @throws {Error} If a handler is already attached to this subscription.
7354
- */
7355
- on(handler) {
7356
- if (this.handler) {
7357
- throw new Error("A handler is already attached to this subscription.");
7358
- }
7359
- this.handler = handler;
7360
- while (this.buffer.length > 0) {
7361
- const event = this.buffer.shift();
7362
- if (event) {
7363
- this.handler(event);
7364
- }
7365
- }
7366
- }
7367
- /**
7368
- * Sends an unsubscribe request to the node and cleans up local resources.
7369
- * @returns {Promise<boolean>} A Promise that resolves to `true` if the unsubscription was successful.
7370
- */
7371
- async unsubscribe() {
7372
- if (this._isClosed) {
7373
- return true;
7374
- }
7375
- if (this.pendingUnsubscribe) {
7376
- return this.pendingUnsubscribe;
7377
- }
7378
- this.pendingUnsubscribe = (async () => {
7379
- try {
7380
- const success = await this.channel.unsubscribe(this.id);
7381
- if (success) {
7382
- this._isClosed = true;
7383
- this.channel.removeSubscription(this.id);
7384
- this.events.emit("unsubscribe", void 0);
7385
- this.events.clear();
7386
- }
7387
- return success;
7388
- } finally {
7389
- this.pendingUnsubscribe = null;
7390
- }
7391
- })();
7392
- return this.pendingUnsubscribe;
7393
- }
8383
+ // src/channel/v0_10_3/index.ts
8384
+ var SubscriptionChannel3 = class extends SubscriptionChannel {
8385
+ id = "RPC0.10.3-subscriptions";
8386
+ channelSpecVersion = _SupportedRpcVersion.v0_10_3;
7394
8387
  };
7395
8388
 
7396
8389
  // src/channel/ws/ws_0_10.ts
@@ -7400,75 +8393,48 @@ var WebSocketChannel = class {
7400
8393
  * @example 'wss://starknet-sepolia.public.blastapi.io/rpc/v0_8'
7401
8394
  */
7402
8395
  nodeUrl;
7403
- /**
7404
- * The underlying WebSocket instance.
7405
- */
7406
- websocket;
7407
- // Store the WebSocket implementation class to allow for custom implementations.
7408
- WsImplementation;
7409
- // Map of active subscriptions, keyed by their ID.
7410
- activeSubscriptions = /* @__PURE__ */ new Map();
7411
- maxBufferSize;
7412
- autoReconnect;
7413
- reconnectOptions;
7414
- requestTimeout;
7415
- isReconnecting = false;
7416
- reconnectAttempts = 0;
7417
- userInitiatedClose = false;
7418
- reconnectTimeoutId = null;
7419
- // Fires once a (re)connection has stayed open long enough to be considered stable,
7420
- // resetting the reconnection attempt counter.
7421
- reconnectStabilityTimeoutId = null;
7422
- requestQueue = [];
7423
- /** Abort handles for the requests currently on the wire, one per pending `sendReceive`. */
7424
- inFlight = /* @__PURE__ */ new Set();
7425
- /**
7426
- * Callers blocked in `waitForUnsubscription`, keyed by subscription id.
7427
- *
7428
- * Held here rather than as `unsubscribe` event listeners because that event only ever
7429
- * announces success: a waiter attached to it cannot learn that the node refused the
7430
- * unsubscribe or that the connection went away, and would wait forever with no timeout of
7431
- * its own to fall back on.
7432
- */
7433
- unsubscribeWaiters = /* @__PURE__ */ new Map();
8396
+ /** Carries the frames and owns the socket lifecycle. */
8397
+ transport;
8398
+ /** Owns `activeSubscriptions`, the unsubscribe waiters and the five subscribe methods. */
8399
+ subscriptionChannel;
7434
8400
  events = new EventEmitter();
7435
- openListener = (ev) => {
7436
- this.scheduleReconnectAttemptsReset();
7437
- this.events.emit("open", ev);
7438
- };
7439
- closeListener = this.onCloseProxy.bind(this);
7440
- messageListener = this.onMessageProxy.bind(this);
7441
- errorListener = (ev) => this.events.emit("error", ev);
7442
8401
  /**
7443
- * JSON RPC latest sent message ID.
7444
- * The receiving message is expected to contain the same ID.
8402
+ * The underlying WebSocket instance.
8403
+ *
8404
+ * A getter rather than a field: a reconnection replaces the socket, and callers — including
8405
+ * the test suite, which spies on `channel.websocket.send` — must see the live one.
8406
+ */
8407
+ get websocket() {
8408
+ return this.transport.socket;
8409
+ }
8410
+ /**
8411
+ * Map of active subscriptions, keyed by their ID.
8412
+ * @internal
7445
8413
  */
7446
- sendId = 0;
8414
+ get activeSubscriptions() {
8415
+ return this.subscriptionChannel.subscriptions;
8416
+ }
7447
8417
  /**
7448
8418
  * Creates an instance of WebSocketChannel.
7449
8419
  * @param {WebSocketOptions} options - The options for configuring the channel.
7450
8420
  */
7451
8421
  constructor(options) {
7452
8422
  this.nodeUrl = options.nodeUrl;
7453
- this.maxBufferSize = options.maxBufferSize ?? 1e3;
7454
- this.autoReconnect = options.autoReconnect ?? true;
7455
- this.reconnectOptions = {
7456
- retries: options.reconnectOptions?.retries ?? 5,
7457
- delay: options.reconnectOptions?.delay ?? 2e3,
7458
- exponential: options.reconnectOptions?.exponential ?? true,
7459
- stableConnectionThreshold: options.reconnectOptions?.stableConnectionThreshold ?? 5e3
7460
- };
7461
- this.requestTimeout = options.requestTimeout ?? 6e4;
7462
- this.WsImplementation = options.websocket || config.get("websocket") || ws_default;
7463
- this.websocket = new this.WsImplementation(this.nodeUrl);
7464
- this.websocket.addEventListener("open", this.openListener);
7465
- this.websocket.addEventListener("close", this.closeListener);
7466
- this.websocket.addEventListener("message", this.messageListener);
7467
- this.websocket.addEventListener("error", this.errorListener);
7468
- }
7469
- idResolver(id) {
7470
- if (id !== void 0) return id;
7471
- return this.sendId++;
8423
+ this.transport = new ReconnectingWsTransport({
8424
+ nodeUrl: options.nodeUrl,
8425
+ websocket: options.websocket,
8426
+ requestTimeout: options.requestTimeout,
8427
+ autoReconnect: options.autoReconnect,
8428
+ reconnectOptions: options.reconnectOptions
8429
+ });
8430
+ this.subscriptionChannel = new SubscriptionChannel({
8431
+ transport: this.transport,
8432
+ maxBufferSize: options.maxBufferSize
8433
+ });
8434
+ this.transport.on("open", (event) => this.events.emit("open", event));
8435
+ this.transport.on("close", (event) => this.events.emit("close", event));
8436
+ this.transport.on("error", (event) => this.events.emit("error", event));
8437
+ this.transport.on("message", (event) => this.events.emit("message", event));
7472
8438
  }
7473
8439
  /**
7474
8440
  * Sends a JSON-RPC request over the WebSocket connection without waiting for a response.
@@ -7485,7 +8451,7 @@ var WebSocketChannel = class {
7485
8451
  "WebSocketChannel.send() failed due to socket being disconnected"
7486
8452
  );
7487
8453
  }
7488
- const usedId = this.idResolver(id);
8454
+ const usedId = id !== void 0 ? id : this.transport.allocateRequestId();
7489
8455
  const rpcRequestBody = {
7490
8456
  id: usedId,
7491
8457
  jsonrpc: "2.0",
@@ -7495,6 +8461,22 @@ var WebSocketChannel = class {
7495
8461
  this.websocket.send(stringify2(rpcRequestBody));
7496
8462
  return usedId;
7497
8463
  }
8464
+ /**
8465
+ * Refuses a call that has nowhere to go, the way `send()` used to.
8466
+ *
8467
+ * Every method below used to reach the socket through `send()`, which threw this exact error
8468
+ * when the connection was down and nothing was going to pick the request up later. The refusal
8469
+ * now originates in the transport, which is generic and rightly words it differently — so the
8470
+ * decision is reproduced here instead, at the boundary where the legacy contract applies.
8471
+ *
8472
+ * The condition is the transport's own: it refuses only when the request would not be queued.
8473
+ */
8474
+ assertSendable() {
8475
+ if (this.isConnected() || this.transport.queuesRequests) return;
8476
+ throw new WebSocketNotConnectedError(
8477
+ "WebSocketChannel.send() failed due to socket being disconnected"
8478
+ );
8479
+ }
7498
8480
  /**
7499
8481
  * Sends a JSON-RPC request and returns a Promise that resolves with the result.
7500
8482
  * This method abstracts the request/response cycle over WebSockets.
@@ -7506,84 +8488,25 @@ var WebSocketChannel = class {
7506
8488
  * @throws {TimeoutError} If the request does not receive a response within the configured `requestTimeout`.
7507
8489
  * @throws {WebSocketNotConnectedError} If the WebSocket is not connected and auto-reconnect is disabled.
7508
8490
  */
7509
- sendReceive(method, params) {
7510
- if (this.isReconnecting || !this.isConnected() && this.autoReconnect && !this.userInitiatedClose) {
7511
- logger.info(`WebSocket: Connection unavailable, queueing request: ${method}`);
7512
- return new Promise((resolve, reject) => {
7513
- this.requestQueue.push({ method, params, resolve, reject });
7514
- });
7515
- }
7516
- const sendId = this.send(method, params);
7517
- const socket = this.websocket;
7518
- return new Promise((resolve, reject) => {
7519
- if (socket.readyState !== ws_default.OPEN) {
7520
- reject(new WebSocketNotConnectedError("WebSocket not available or not connected."));
7521
- return;
7522
- }
7523
- let timeoutId;
7524
- const settle = () => {
7525
- clearTimeout(timeoutId);
7526
- socket.removeEventListener("message", messageHandler);
7527
- socket.removeEventListener("error", errorHandler);
7528
- this.inFlight.delete(abort);
7529
- };
7530
- const messageHandler = (event) => {
7531
- if (!isString(event.data)) {
7532
- logger.warn("WebSocket received non-string message data:", event.data);
7533
- return;
7534
- }
7535
- let message;
7536
- try {
7537
- message = JSON.parse(event.data);
7538
- } catch (error) {
7539
- logger.error(
7540
- `WebSocketChannel: Error parsing incoming message: ${event.data}, Error: ${error}`
7541
- );
7542
- return;
7543
- }
7544
- if (message.id !== sendId) return;
7545
- settle();
7546
- if ("result" in message) {
7547
- resolve(message.result);
7548
- } else {
7549
- reject(new Error(`Error on ${method} (id: ${sendId}): ${JSON.stringify(message.error)}`));
7550
- }
7551
- };
7552
- const errorHandler = (event) => {
7553
- settle();
7554
- reject(
7555
- new Error(
7556
- `WebSocket error during ${method} (id: ${sendId}): ${event.type || "Unknown error"}`
7557
- )
7558
- );
7559
- };
7560
- const abort = (reason) => {
7561
- settle();
7562
- reject(
7563
- new WebSocketNotConnectedError(
7564
- `Request ${method} (id: ${sendId}) went unanswered: ${reason}`
7565
- )
7566
- );
7567
- };
7568
- socket.addEventListener("message", messageHandler);
7569
- socket.addEventListener("error", errorHandler);
7570
- this.inFlight.add(abort);
7571
- timeoutId = setTimeout(() => {
7572
- settle();
7573
- reject(
7574
- new TimeoutError(
7575
- `Request ${method} (id: ${sendId}) timed out after ${this.requestTimeout}ms`
7576
- )
7577
- );
7578
- }, this.requestTimeout);
8491
+ async sendReceive(method, params) {
8492
+ this.assertSendable();
8493
+ const response = await this.transport.request({
8494
+ id: 0,
8495
+ jsonrpc: "2.0",
8496
+ method,
8497
+ ...params && { params }
7579
8498
  });
8499
+ if (response.error) {
8500
+ throw new Error(`Error on ${method}: ${JSON.stringify(response.error)}`);
8501
+ }
8502
+ return response.result;
7580
8503
  }
7581
8504
  /**
7582
8505
  * Checks if the WebSocket connection is currently open.
7583
8506
  * @returns {boolean} `true` if the connection is open, `false` otherwise.
7584
8507
  */
7585
8508
  isConnected() {
7586
- return this.websocket.readyState === ws_default.OPEN;
8509
+ return this.transport.isConnected();
7587
8510
  }
7588
8511
  /**
7589
8512
  * Returns a Promise that resolves when the WebSocket connection is open.
@@ -7597,16 +8520,21 @@ var WebSocketChannel = class {
7597
8520
  * ```
7598
8521
  */
7599
8522
  async waitForConnection() {
7600
- if (this.websocket.readyState !== ws_default.OPEN) {
7601
- return new Promise((resolve, reject) => {
7602
- if (!this.websocket) return;
7603
- this.websocket.onopen = () => resolve(this.websocket.readyState);
7604
- this.websocket.onerror = (error) => {
7605
- return reject(error);
7606
- };
8523
+ if (this.isConnected()) return this.websocket.readyState;
8524
+ return new Promise((resolve, reject) => {
8525
+ const settle = () => {
8526
+ offOpen();
8527
+ offError();
8528
+ };
8529
+ const offOpen = this.transport.on("open", () => {
8530
+ settle();
8531
+ resolve(this.websocket.readyState);
7607
8532
  });
7608
- }
7609
- return this.websocket.readyState;
8533
+ const offError = this.transport.on("error", (event) => {
8534
+ settle();
8535
+ reject(event);
8536
+ });
8537
+ });
7610
8538
  }
7611
8539
  /**
7612
8540
  * Closes the WebSocket connection.
@@ -7615,34 +8543,29 @@ var WebSocketChannel = class {
7615
8543
  * @param {string} [reason] - The WebSocket connection close reason.
7616
8544
  */
7617
8545
  disconnect(code, reason) {
7618
- if (this.reconnectTimeoutId) {
7619
- clearTimeout(this.reconnectTimeoutId);
7620
- this.reconnectTimeoutId = null;
7621
- }
7622
- if (this.reconnectStabilityTimeoutId) {
7623
- clearTimeout(this.reconnectStabilityTimeoutId);
7624
- this.reconnectStabilityTimeoutId = null;
7625
- }
7626
- this.userInitiatedClose = true;
7627
- this.isReconnecting = false;
7628
- this._rejectRequestQueue("the connection was closed by the user");
7629
- this._rejectInFlight("the connection was closed by the user");
7630
- this._rejectUnsubscribeWaiters("the connection was closed by the user");
7631
- this.websocket.close(code, reason);
8546
+ this.transport.close(code, reason);
7632
8547
  }
7633
8548
  /**
7634
8549
  * Returns a Promise that resolves when the WebSocket connection is closed.
7635
8550
  * @returns {Promise<number | Event>} A Promise that resolves with the WebSocket's `readyState` or a `CloseEvent` when disconnected.
7636
8551
  */
7637
8552
  async waitForDisconnection() {
7638
- if (this.websocket.readyState !== ws_default.CLOSED) {
7639
- return new Promise((resolve, reject) => {
7640
- if (!this.websocket) return;
7641
- this.websocket.onclose = () => resolve(this.websocket.readyState);
7642
- this.websocket.onerror = reject;
8553
+ if (this.websocket.readyState === ws_default.CLOSED) return this.websocket.readyState;
8554
+ return new Promise((resolve, reject) => {
8555
+ const settle = () => {
8556
+ offClose();
8557
+ offError();
8558
+ };
8559
+ const offClose = this.transport.on("close", () => {
8560
+ settle();
8561
+ resolve(this.websocket.readyState);
7643
8562
  });
7644
- }
7645
- return this.websocket.readyState;
8563
+ const offError = this.transport.on("error", (event) => {
8564
+ if (this.websocket.readyState !== ws_default.OPEN) return;
8565
+ settle();
8566
+ reject(event);
8567
+ });
8568
+ });
7646
8569
  }
7647
8570
  /**
7648
8571
  * Unsubscribes from a Starknet subscription.
@@ -7652,48 +8575,13 @@ var WebSocketChannel = class {
7652
8575
  * @returns {Promise<boolean>} A Promise that resolves with `true` if the unsubscription was successful.
7653
8576
  */
7654
8577
  async unsubscribe(subscriptionId) {
7655
- let status;
7656
- try {
7657
- status = await this.sendReceive("starknet_unsubscribe", {
7658
- subscription_id: subscriptionId
7659
- });
7660
- } catch (error) {
7661
- this._settleUnsubscribeWaiters(subscriptionId, error);
7662
- throw error;
7663
- }
8578
+ this.assertSendable();
8579
+ const status = await this.subscriptionChannel.unsubscribe(subscriptionId);
7664
8580
  if (status) {
7665
8581
  this.events.emit("unsubscribe", subscriptionId);
7666
- this._settleUnsubscribeWaiters(subscriptionId);
7667
- } else {
7668
- this._settleUnsubscribeWaiters(
7669
- subscriptionId,
7670
- new Error(`Node refused to unsubscribe subscription ${subscriptionId}`)
7671
- );
7672
8582
  }
7673
8583
  return status;
7674
8584
  }
7675
- /** Settles every caller waiting on one subscription id: resolved, or rejected with `error`. */
7676
- _settleUnsubscribeWaiters(subscriptionId, error) {
7677
- const waiters = this.unsubscribeWaiters.get(subscriptionId);
7678
- if (!waiters) return;
7679
- this.unsubscribeWaiters.delete(subscriptionId);
7680
- waiters.forEach((waiter) => error ? waiter.reject(error) : waiter.resolve());
7681
- }
7682
- /**
7683
- * Rejects every caller still waiting on any subscription.
7684
- *
7685
- * Once the connection is gone, no unsubscribe can be observed on it: a reconnection restores
7686
- * each subscription under a fresh id, so the id being waited on will never be announced.
7687
- */
7688
- _rejectUnsubscribeWaiters(reason) {
7689
- if (this.unsubscribeWaiters.size === 0) return;
7690
- Array.from(this.unsubscribeWaiters.keys()).forEach(
7691
- (id) => this._settleUnsubscribeWaiters(
7692
- id,
7693
- new WebSocketNotConnectedError(`Subscription ${id} was never unsubscribed: ${reason}`)
7694
- )
7695
- );
7696
- }
7697
8585
  /**
7698
8586
  * Returns a Promise that resolves when a specific subscription is successfully unsubscribed.
7699
8587
  * @param {SUBSCRIPTION_ID} targetId - The ID of the subscription to wait for.
@@ -7705,182 +8593,14 @@ var WebSocketChannel = class {
7705
8593
  * ```
7706
8594
  */
7707
8595
  waitForUnsubscription(targetId) {
7708
- return new Promise((resolve, reject) => {
7709
- const waiters = this.unsubscribeWaiters.get(targetId) ?? /* @__PURE__ */ new Set();
7710
- waiters.add({ resolve, reject });
7711
- this.unsubscribeWaiters.set(targetId, waiters);
7712
- });
8596
+ return this.subscriptionChannel.waitForUnsubscription(targetId);
7713
8597
  }
7714
8598
  /**
7715
8599
  * Manually initiates a reconnection attempt.
7716
8600
  * This creates a new WebSocket instance and re-establishes listeners.
7717
8601
  */
7718
8602
  reconnect() {
7719
- this.userInitiatedClose = false;
7720
- this.websocket = new this.WsImplementation(this.nodeUrl);
7721
- this.websocket.addEventListener("open", this.openListener);
7722
- this.websocket.addEventListener("close", this.closeListener);
7723
- this.websocket.addEventListener("message", this.messageListener);
7724
- this.websocket.addEventListener("error", this.errorListener);
7725
- }
7726
- _processRequestQueue() {
7727
- const pending = this.requestQueue;
7728
- this.requestQueue = [];
7729
- logger.info(`WebSocket: Processing ${pending.length} queued requests.`);
7730
- pending.forEach(({ method, params, resolve, reject }) => {
7731
- this.sendReceive(method, params).then(resolve).catch(reject);
7732
- });
7733
- }
7734
- /**
7735
- * Reject every request still waiting in the queue.
7736
- *
7737
- * A queued request carries no timeout of its own: the `requestTimeout` timer is only
7738
- * armed once the request is actually put on the wire. So once the channel reaches a state
7739
- * where the queue can never be flushed — reconnection gave up, or the user closed the
7740
- * connection — the queued promises would stay pending forever, and no caller-side timeout
7741
- * could rescue them. Settling them here is the only way out.
7742
- */
7743
- _rejectRequestQueue(reason) {
7744
- if (this.requestQueue.length === 0) return;
7745
- const pending = this.requestQueue;
7746
- this.requestQueue = [];
7747
- logger.info(`WebSocket: Rejecting ${pending.length} queued request(s). Reason: ${reason}.`);
7748
- pending.forEach(({ method, reject }) => {
7749
- reject(new WebSocketNotConnectedError(`Request ${method} was never sent: ${reason}`));
7750
- });
7751
- }
7752
- /**
7753
- * Settle every request already on the wire.
7754
- *
7755
- * The counterpart of `_rejectRequestQueue`, for requests past the queue. Their only other
7756
- * exit is the `requestTimeout` timer, so without this the caller waits the whole timeout —
7757
- * 60s by default — for a reply that can no longer arrive, and that pending timer keeps the
7758
- * Node event loop alive for just as long.
7759
- */
7760
- _rejectInFlight(reason) {
7761
- if (this.inFlight.size === 0) return;
7762
- const pending = Array.from(this.inFlight);
7763
- this.inFlight.clear();
7764
- logger.info(`WebSocket: Rejecting ${pending.length} in-flight request(s). Reason: ${reason}.`);
7765
- pending.forEach((abort) => abort(reason));
7766
- }
7767
- async _restoreSubscriptions() {
7768
- const oldSubscriptions = Array.from(this.activeSubscriptions.values());
7769
- this.activeSubscriptions.clear();
7770
- const restorePromises = oldSubscriptions.map(async (sub) => {
7771
- try {
7772
- const newSubId = await this.sendReceive(sub.method, sub.params);
7773
- sub.id = newSubId;
7774
- this.activeSubscriptions.set(newSubId, sub);
7775
- logger.info(`Subscription ${sub.method} restored with new ID: ${newSubId}`);
7776
- } catch (error) {
7777
- logger.error(`Failed to restore subscription ${sub.method}:`, error);
7778
- sub._markClosed();
7779
- }
7780
- });
7781
- await Promise.all(restorePromises);
7782
- }
7783
- /**
7784
- * Reset the reconnection attempt counter, but only once the current connection has
7785
- * stayed open for `stableConnectionThreshold` ms. A connection that opens and then
7786
- * immediately drops (a flapping gateway) never reaches this point, so its attempts
7787
- * keep accumulating toward the retry cap instead of resetting every cycle.
7788
- */
7789
- scheduleReconnectAttemptsReset() {
7790
- if (this.reconnectStabilityTimeoutId) {
7791
- clearTimeout(this.reconnectStabilityTimeoutId);
7792
- }
7793
- this.reconnectStabilityTimeoutId = setTimeout(() => {
7794
- this.reconnectAttempts = 0;
7795
- this.reconnectStabilityTimeoutId = null;
7796
- }, this.reconnectOptions.stableConnectionThreshold);
7797
- }
7798
- _startReconnect() {
7799
- if (this.isReconnecting || !this.autoReconnect) {
7800
- return;
7801
- }
7802
- if (this.reconnectStabilityTimeoutId) {
7803
- clearTimeout(this.reconnectStabilityTimeoutId);
7804
- this.reconnectStabilityTimeoutId = null;
7805
- }
7806
- this.isReconnecting = true;
7807
- const tryReconnect = () => {
7808
- if (this.reconnectAttempts >= this.reconnectOptions.retries) {
7809
- logger.error("WebSocket: Maximum reconnection retries reached. Giving up.");
7810
- this.isReconnecting = false;
7811
- this._rejectRequestQueue(
7812
- `reconnection gave up after ${this.reconnectOptions.retries} attempts`
7813
- );
7814
- return;
7815
- }
7816
- this.reconnectAttempts += 1;
7817
- logger.info(
7818
- `WebSocket: Connection lost. Attempting to reconnect... (${this.reconnectAttempts}/${this.reconnectOptions.retries})`
7819
- );
7820
- this.reconnect();
7821
- let attemptSettled = false;
7822
- const scheduleRetry = () => {
7823
- if (attemptSettled || !this.isReconnecting) return;
7824
- attemptSettled = true;
7825
- const delay = this.reconnectOptions.exponential ? this.reconnectOptions.delay * 2 ** (this.reconnectAttempts - 1) : this.reconnectOptions.delay;
7826
- logger.info(`WebSocket: Reconnect attempt failed. Retrying in ${delay}ms.`);
7827
- this.reconnectTimeoutId = setTimeout(tryReconnect, delay);
7828
- };
7829
- this.websocket.onopen = async () => {
7830
- logger.info("WebSocket: Reconnection successful.");
7831
- attemptSettled = true;
7832
- this.isReconnecting = false;
7833
- await this._restoreSubscriptions();
7834
- this._processRequestQueue();
7835
- this.events.emit("open", new Event("open"));
7836
- };
7837
- this.websocket.onerror = scheduleRetry;
7838
- this.websocket.addEventListener("close", scheduleRetry);
7839
- };
7840
- tryReconnect();
7841
- }
7842
- onCloseProxy(ev) {
7843
- this.websocket.removeEventListener("open", this.openListener);
7844
- this.websocket.removeEventListener("close", this.closeListener);
7845
- this.websocket.removeEventListener("message", this.messageListener);
7846
- this.websocket.removeEventListener("error", this.errorListener);
7847
- this._rejectInFlight("the connection was closed");
7848
- this._rejectUnsubscribeWaiters("the connection was closed");
7849
- this.events.emit("close", ev);
7850
- if (!this.userInitiatedClose) {
7851
- this._startReconnect();
7852
- }
7853
- }
7854
- onMessageProxy(event) {
7855
- let message;
7856
- try {
7857
- message = JSON.parse(event.data);
7858
- } catch (error) {
7859
- logger.error(
7860
- `WebSocketChannel: Error parsing incoming message: ${event.data}, Error: ${error}`
7861
- );
7862
- return;
7863
- }
7864
- if (message.method && isObject(message.params) && "subscription_id" in message.params) {
7865
- const { result, subscription_id } = message.params;
7866
- const subscription = this.activeSubscriptions.get(subscription_id);
7867
- if (subscription) {
7868
- subscription._handleEvent(result);
7869
- } else {
7870
- queueMicrotask(() => {
7871
- const registered = this.activeSubscriptions.get(subscription_id);
7872
- if (registered) {
7873
- registered._handleEvent(result);
7874
- } else {
7875
- logger.warn(
7876
- `WebSocketChannel: Received event for untracked subscription ID: ${subscription_id}.`
7877
- );
7878
- }
7879
- });
7880
- }
7881
- }
7882
- logger.debug("onMessageProxy:", event.data);
7883
- this.events.emit("message", event);
8603
+ this.transport.reconnect();
7884
8604
  }
7885
8605
  /**
7886
8606
  * Subscribes to new block headers.
@@ -7888,20 +8608,8 @@ var WebSocketChannel = class {
7888
8608
  * @returns {Promise<Subscription<BLOCK_HEADER>>} A Promise that resolves with a `Subscription` object for new block headers.
7889
8609
  */
7890
8610
  async subscribeNewHeads(params = {}) {
7891
- const method = "starknet_subscribeNewHeads";
7892
- const rpcParams = {
7893
- block_id: params.blockIdentifier ? new Block(params.blockIdentifier).identifier : void 0
7894
- };
7895
- const subId = await this.sendReceive(method, rpcParams);
7896
- const subscription = new Subscription({
7897
- channel: this,
7898
- method,
7899
- params: rpcParams,
7900
- id: subId,
7901
- maxBufferSize: this.maxBufferSize
7902
- });
7903
- this.activeSubscriptions.set(subId, subscription);
7904
- return subscription;
8611
+ this.assertSendable();
8612
+ return this.subscriptionChannel.subscribeNewHeads(params);
7905
8613
  }
7906
8614
  /**
7907
8615
  * Subscribes to events matching a given filter.
@@ -7909,27 +8617,8 @@ var WebSocketChannel = class {
7909
8617
  * @returns {Promise<Subscription<EMITTED_EVENT>>} A Promise that resolves with a `Subscription` object for the specified events.
7910
8618
  */
7911
8619
  async subscribeEvents(params = {}) {
7912
- const method = "starknet_subscribeEvents";
7913
- let from_address;
7914
- if (params.fromAddress !== void 0) {
7915
- from_address = Array.isArray(params.fromAddress) ? bigNumberishArrayToHexadecimalStringArray(params.fromAddress) : toHex(params.fromAddress);
7916
- }
7917
- const rpcParams = {
7918
- from_address,
7919
- keys: params.keys,
7920
- block_id: params.blockIdentifier ? new Block(params.blockIdentifier).identifier : void 0,
7921
- finality_status: params.finalityStatus
7922
- };
7923
- const subId = await this.sendReceive(method, rpcParams);
7924
- const subscription = new Subscription({
7925
- channel: this,
7926
- method,
7927
- params: rpcParams,
7928
- id: subId,
7929
- maxBufferSize: this.maxBufferSize
7930
- });
7931
- this.activeSubscriptions.set(subId, subscription);
7932
- return subscription;
8620
+ this.assertSendable();
8621
+ return this.subscriptionChannel.subscribeEvents(params);
7933
8622
  }
7934
8623
  /**
7935
8624
  * Subscribes to status updates for a specific transaction.
@@ -7937,21 +8626,8 @@ var WebSocketChannel = class {
7937
8626
  * @returns {Promise<Subscription<NEW_TXN_STATUS>>} A Promise that resolves with a `Subscription` object for the transaction's status.
7938
8627
  */
7939
8628
  async subscribeTransactionStatus(params) {
7940
- const method = "starknet_subscribeTransactionStatus";
7941
- const rpcParams = {
7942
- transaction_hash: toHex(params.transactionHash),
7943
- block_id: params.blockIdentifier ? new Block(params.blockIdentifier).identifier : void 0
7944
- };
7945
- const subId = await this.sendReceive(method, rpcParams);
7946
- const subscription = new Subscription({
7947
- channel: this,
7948
- method,
7949
- params: rpcParams,
7950
- id: subId,
7951
- maxBufferSize: this.maxBufferSize
7952
- });
7953
- this.activeSubscriptions.set(subId, subscription);
7954
- return subscription;
8629
+ this.assertSendable();
8630
+ return this.subscriptionChannel.subscribeTransactionStatus(params);
7955
8631
  }
7956
8632
  /**
7957
8633
  * Subscribes to new transaction receipts.
@@ -7959,21 +8635,8 @@ var WebSocketChannel = class {
7959
8635
  * @returns {Promise<Subscription<NewTransactionReceiptsEvent['result']>>} A Promise that resolves with a `Subscription` object for new transaction receipts.
7960
8636
  */
7961
8637
  async subscribeNewTransactionReceipts(params = {}) {
7962
- const method = "starknet_subscribeNewTransactionReceipts";
7963
- const rpcParams = {
7964
- finality_status: params.finalityStatus,
7965
- sender_address: params.senderAddress && bigNumberishArrayToHexadecimalStringArray(params.senderAddress)
7966
- };
7967
- const subId = await this.sendReceive(method, rpcParams);
7968
- const subscription = new Subscription({
7969
- channel: this,
7970
- method,
7971
- params: rpcParams,
7972
- id: subId,
7973
- maxBufferSize: this.maxBufferSize
7974
- });
7975
- this.activeSubscriptions.set(subId, subscription);
7976
- return subscription;
8638
+ this.assertSendable();
8639
+ return this.subscriptionChannel.subscribeNewTransactionReceipts(params);
7977
8640
  }
7978
8641
  /**
7979
8642
  * Subscribes to new transactions.
@@ -7981,29 +8644,15 @@ var WebSocketChannel = class {
7981
8644
  * @returns {Promise<Subscription<NewTransactionEvent['result']>>} A Promise that resolves with a `Subscription` object for new transactions.
7982
8645
  */
7983
8646
  async subscribeNewTransactions(params = {}) {
7984
- const method = "starknet_subscribeNewTransactions";
7985
- const rpcParams = {
7986
- finality_status: params.finalityStatus,
7987
- sender_address: params.senderAddress && bigNumberishArrayToHexadecimalStringArray(params.senderAddress),
7988
- tags: params.tags
7989
- };
7990
- const subId = await this.sendReceive(method, rpcParams);
7991
- const subscription = new Subscription({
7992
- channel: this,
7993
- method,
7994
- params: rpcParams,
7995
- id: subId,
7996
- maxBufferSize: this.maxBufferSize
7997
- });
7998
- this.activeSubscriptions.set(subId, subscription);
7999
- return subscription;
8647
+ this.assertSendable();
8648
+ return this.subscriptionChannel.subscribeNewTransactions(params);
8000
8649
  }
8001
8650
  /**
8002
8651
  * Internal method to remove subscription from active map.
8003
8652
  * @internal
8004
8653
  */
8005
8654
  removeSubscription(id) {
8006
- this.activeSubscriptions.delete(id);
8655
+ this.subscriptionChannel.removeSubscription(id);
8007
8656
  }
8008
8657
  /**
8009
8658
  * Adds a listener for a given event.
@@ -9680,14 +10329,14 @@ var RpcProvider = class {
9680
10329
  const options = optionsOrProvider;
9681
10330
  if (options && options.specVersion) {
9682
10331
  if (isVersion("0.9", options.specVersion)) {
9683
- this.channel = new rpc_0_9_0_exports.RpcChannel({ ...options, waitMode: false });
10332
+ this.channel = new v0_9_exports.RpcChannel({ ...options, waitMode: false });
9684
10333
  } else if (isVersion("0.10", options.specVersion)) {
9685
- this.channel = new rpc_0_10_3_exports.RpcChannel({ ...options, waitMode: false });
10334
+ this.channel = new v0_10_3_exports.RpcChannel({ ...options, waitMode: false });
9686
10335
  } else throw new Error(`unsupported channel for spec version: ${options.specVersion}`);
9687
10336
  } else if (isVersion("0.9", config.get("rpcVersion"))) {
9688
- this.channel = new rpc_0_9_0_exports.RpcChannel({ ...options, waitMode: false });
10337
+ this.channel = new v0_9_exports.RpcChannel({ ...options, waitMode: false });
9689
10338
  } else if (isVersion("0.10", config.get("rpcVersion"))) {
9690
- this.channel = new rpc_0_10_3_exports.RpcChannel({ ...options, waitMode: false });
10339
+ this.channel = new v0_10_3_exports.RpcChannel({ ...options, waitMode: false });
9691
10340
  } else throw new Error("unable to define spec version for channel");
9692
10341
  this.responseParser = new RPCResponseParser(options?.resourceBoundsOverhead);
9693
10342
  const plugins = options?.plugins === false ? [] : options?.plugins ?? defaultPlugins;
@@ -9702,7 +10351,7 @@ var RpcProvider = class {
9702
10351
  */
9703
10352
  // NOTE: the generic T and 'this' reference are used so that the expanded class is generated when a mixin is applied
9704
10353
  static async create(optionsOrProvider) {
9705
- const channel = new rpc_0_9_0_exports.RpcChannel({ ...optionsOrProvider });
10354
+ const channel = new v0_9_exports.RpcChannel({ ...optionsOrProvider });
9706
10355
  const spec = await channel.getSpecVersion();
9707
10356
  if (!isSupportedSpecVersion(spec)) {
9708
10357
  logger.warn(`Using incompatible node spec version ${spec}`);
@@ -9780,7 +10429,7 @@ var RpcProvider = class {
9780
10429
  return this.channel.getBlockWithTxHashes(blockIdentifier);
9781
10430
  }
9782
10431
  async getBlockWithTxs(blockIdentifier, options) {
9783
- if (this.channel instanceof rpc_0_10_2_exports.RpcChannel) {
10432
+ if (this.channel instanceof v0_10_2_exports.RpcChannel) {
9784
10433
  return this.channel.getBlockWithTxs(blockIdentifier, options);
9785
10434
  }
9786
10435
  return this.channel.getBlockWithTxs(blockIdentifier);
@@ -9837,7 +10486,7 @@ var RpcProvider = class {
9837
10486
  return solidityUint256PackedKeccak256(params);
9838
10487
  }
9839
10488
  async getBlockWithReceipts(blockIdentifier, options) {
9840
- if (this.channel instanceof rpc_0_10_2_exports.RpcChannel) {
10489
+ if (this.channel instanceof v0_10_2_exports.RpcChannel) {
9841
10490
  return this.channel.getBlockWithReceipts(blockIdentifier, options);
9842
10491
  }
9843
10492
  return this.channel.getBlockWithReceipts(blockIdentifier);
@@ -9847,7 +10496,7 @@ var RpcProvider = class {
9847
10496
  return this.channel.getBlockStateUpdate(blockIdentifier, contractAddresses);
9848
10497
  }
9849
10498
  async getBlockTransactionsTraces(blockIdentifier, options) {
9850
- if (this.channel instanceof rpc_0_10_2_exports.RpcChannel) {
10499
+ if (this.channel instanceof v0_10_2_exports.RpcChannel) {
9851
10500
  return this.channel.getBlockTransactionsTraces(blockIdentifier, options);
9852
10501
  }
9853
10502
  return this.channel.getBlockTransactionsTraces(blockIdentifier);
@@ -9859,13 +10508,13 @@ var RpcProvider = class {
9859
10508
  return this.getTransactionByHash(txHash, options);
9860
10509
  }
9861
10510
  async getTransactionByHash(txHash, options) {
9862
- if (this.channel instanceof rpc_0_10_2_exports.RpcChannel) {
10511
+ if (this.channel instanceof v0_10_2_exports.RpcChannel) {
9863
10512
  return this.channel.getTransactionByHash(txHash, options);
9864
10513
  }
9865
10514
  return this.channel.getTransactionByHash(txHash);
9866
10515
  }
9867
10516
  async getTransactionByBlockIdAndIndex(blockIdentifier, index, options) {
9868
- if (this.channel instanceof rpc_0_10_2_exports.RpcChannel) {
10517
+ if (this.channel instanceof v0_10_2_exports.RpcChannel) {
9869
10518
  return this.channel.getTransactionByBlockIdAndIndex(blockIdentifier, index, options);
9870
10519
  }
9871
10520
  return this.channel.getTransactionByBlockIdAndIndex(blockIdentifier, index);
@@ -10001,10 +10650,10 @@ var RpcProvider = class {
10001
10650
  return this.channel.getSyncingStats();
10002
10651
  }
10003
10652
  async getEvents(eventFilter) {
10004
- if (this.channel instanceof rpc_0_10_2_exports.RpcChannel) {
10653
+ if (this.channel instanceof v0_10_2_exports.RpcChannel) {
10005
10654
  return this.channel.getEvents(eventFilter);
10006
10655
  }
10007
- if (this.channel instanceof rpc_0_9_0_exports.RpcChannel) {
10656
+ if (this.channel instanceof v0_9_exports.RpcChannel) {
10008
10657
  return this.channel.getEvents(eventFilter);
10009
10658
  }
10010
10659
  throw new Error("Unsupported channel type");
@@ -10081,6 +10730,92 @@ var RpcProvider = class {
10081
10730
  var ProviderInterface = class {
10082
10731
  };
10083
10732
 
10733
+ // src/provider/ws.ts
10734
+ var WebSocketProvider = class _WebSocketProvider extends RpcProvider {
10735
+ /** The socket both channels write to. Owned by this provider only when it built it. */
10736
+ transport;
10737
+ /** The `starknet_subscribe*` surface, on the same spec version as `channel`. */
10738
+ subscriptions;
10739
+ constructor(options) {
10740
+ const transport = options.transport ?? _WebSocketProvider.buildTransport(options);
10741
+ super({ ...options, transport });
10742
+ this.transport = transport;
10743
+ this.subscriptions = _WebSocketProvider.buildSubscriptionChannel(options, transport);
10744
+ }
10745
+ /**
10746
+ * Closes the socket when the provider leaves a `using` scope.
10747
+ *
10748
+ * Written as a normal method rather than a computed class member on purpose: a computed
10749
+ * `[Symbol.dispose]` is evaluated while the class is being defined, so on a runtime without the
10750
+ * symbol — older browsers the IIFE build targets — the whole module would throw on load, for
10751
+ * callers who never asked for `using`. Assigning to the prototype behind a guard keeps the
10752
+ * failure confined to the feature that needs it.
10753
+ */
10754
+ dispose() {
10755
+ this.transport.close();
10756
+ }
10757
+ static buildTransport(options) {
10758
+ if (!options.nodeUrl) {
10759
+ throw new LibraryError("WebSocketProvider requires either a nodeUrl or a transport");
10760
+ }
10761
+ return new ReconnectingWsTransport({
10762
+ ...options,
10763
+ nodeUrl: options.nodeUrl
10764
+ });
10765
+ }
10766
+ /**
10767
+ * Resolves the subscription channel the same way `RpcProvider` resolves its request channel
10768
+ * (`rpc.ts`), branch for branch including the `config.get('rpcVersion')` fallback. Any
10769
+ * divergence here would pair channels of two different specs on one socket, which is exactly
10770
+ * what the per-version namespaces exist to prevent.
10771
+ */
10772
+ static buildSubscriptionChannel(options, transport) {
10773
+ const channelOptions = { transport, maxBufferSize: options.maxBufferSize };
10774
+ const forVersion = (specVersion) => {
10775
+ if (isVersion("0.9", specVersion)) return new v0_9_exports.SubscriptionChannel(channelOptions);
10776
+ if (isVersion("0.10", specVersion)) return new v0_10_3_exports.SubscriptionChannel(channelOptions);
10777
+ throw new LibraryError(`unsupported subscription channel for spec version: ${specVersion}`);
10778
+ };
10779
+ if (options.specVersion) return forVersion(options.specVersion);
10780
+ return forVersion(config.get("rpcVersion"));
10781
+ }
10782
+ /**
10783
+ * Probes the node for its spec version, then builds the matching pair of channels.
10784
+ *
10785
+ * The transport is created before the probe and handed to the result, so the socket survives
10786
+ * the probe channel being discarded — the reason the transport is a separate value at all.
10787
+ */
10788
+ // `this` is deliberately untyped. The base declares `create` with a `this` constructor type so
10789
+ // that a mixin-expanded class is generated; repeating that here with a narrower constructor
10790
+ // makes the derived static side unassignable to the base's. Mirroring the base's parameter and
10791
+ // return shapes keeps the override legal, and the cast below is checked by the tests.
10792
+ //
10793
+ // `T` defaults to `WebSocketProvider` because untying `this` also unties the inference: with no
10794
+ // contextual type, `T` would otherwise fall back to its constraint and hand the caller a plain
10795
+ // `RpcProvider`, on which `subscriptions` does not exist. An annotated target still wins.
10796
+ static async create(optionsOrProvider) {
10797
+ const options = optionsOrProvider ?? {};
10798
+ const transport = options.transport ?? _WebSocketProvider.buildTransport(options);
10799
+ const probe = new v0_9_exports.RpcChannel({ ...options, transport });
10800
+ const spec = await probe.getSpecVersion();
10801
+ if (!isSupportedSpecVersion(spec)) {
10802
+ logger.warn(`Using incompatible node spec version ${spec}`);
10803
+ }
10804
+ if (isVersion("0.9", spec)) {
10805
+ return new this({ ...options, transport, specVersion: _SupportedRpcVersion.v0_9_0 });
10806
+ }
10807
+ if (isVersion("0.10", spec)) {
10808
+ return new this({ ...options, transport, specVersion: _SupportedRpcVersion.v0_10_3 });
10809
+ }
10810
+ throw new LibraryError(
10811
+ `Provided RPC node specification version ${spec} is not compatible with the SDK. SDK supported RPC versions ${Object.keys(_SupportedRpcVersion).toString()}`
10812
+ );
10813
+ }
10814
+ };
10815
+ if (typeof Symbol.dispose !== "undefined") {
10816
+ WebSocketProvider.prototype[Symbol.dispose] = WebSocketProvider.prototype.dispose;
10817
+ }
10818
+
10084
10819
  // src/signer/interface.ts
10085
10820
  var SignerInterface = class {
10086
10821
  };
@@ -10155,7 +10890,7 @@ var Signer = class {
10155
10890
  };
10156
10891
 
10157
10892
  // src/signer/ethSigner.ts
10158
- import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1";
10893
+ import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
10159
10894
 
10160
10895
  // src/utils/uint256.ts
10161
10896
  var uint256_exports = {};
@@ -10187,14 +10922,17 @@ var EthSigner = class {
10187
10922
  */
10188
10923
  async getPubKey() {
10189
10924
  return addHexPrefix(
10190
- buf2hex(secp256k12.getPublicKey(this.pk, false)).padStart(130, "0").slice(2)
10925
+ buf2hex(secp256k12.getPublicKey(hexToBytes(addHexPrefix(this.pk)), false)).padStart(130, "0").slice(2)
10191
10926
  );
10192
10927
  }
10193
10928
  async signMessage(typedData, accountAddress) {
10194
10929
  const msgHash = getMessageHash(typedData, accountAddress);
10195
- const signature = secp256k12.sign(
10196
- removeHexPrefix(sanitizeHex(msgHash)),
10197
- this.pk
10930
+ const signature = secp256k12.Signature.fromBytes(
10931
+ secp256k12.sign(hexToBytes(sanitizeHex(msgHash)), hexToBytes(addHexPrefix(this.pk)), {
10932
+ prehash: false,
10933
+ format: "recovered"
10934
+ }),
10935
+ "recovered"
10198
10936
  );
10199
10937
  return this.formatEthSignature(signature);
10200
10938
  }
@@ -10214,9 +10952,12 @@ var EthSigner = class {
10214
10952
  } else {
10215
10953
  throw Error("unsupported signTransaction version");
10216
10954
  }
10217
- const signature = secp256k12.sign(
10218
- removeHexPrefix(sanitizeHex(msgHash)),
10219
- this.pk
10955
+ const signature = secp256k12.Signature.fromBytes(
10956
+ secp256k12.sign(hexToBytes(sanitizeHex(msgHash)), hexToBytes(addHexPrefix(this.pk)), {
10957
+ prehash: false,
10958
+ format: "recovered"
10959
+ }),
10960
+ "recovered"
10220
10961
  );
10221
10962
  return this.formatEthSignature(signature);
10222
10963
  }
@@ -10236,9 +10977,12 @@ var EthSigner = class {
10236
10977
  } else {
10237
10978
  throw Error("unsupported signDeployAccountTransaction version");
10238
10979
  }
10239
- const signature = secp256k12.sign(
10240
- removeHexPrefix(sanitizeHex(msgHash)),
10241
- this.pk
10980
+ const signature = secp256k12.Signature.fromBytes(
10981
+ secp256k12.sign(hexToBytes(sanitizeHex(msgHash)), hexToBytes(addHexPrefix(this.pk)), {
10982
+ prehash: false,
10983
+ format: "recovered"
10984
+ }),
10985
+ "recovered"
10242
10986
  );
10243
10987
  return this.formatEthSignature(signature);
10244
10988
  }
@@ -10255,9 +10999,12 @@ var EthSigner = class {
10255
10999
  } else {
10256
11000
  throw Error("unsupported signDeclareTransaction version");
10257
11001
  }
10258
- const signature = secp256k12.sign(
10259
- removeHexPrefix(sanitizeHex(msgHash)),
10260
- this.pk
11002
+ const signature = secp256k12.Signature.fromBytes(
11003
+ secp256k12.sign(hexToBytes(sanitizeHex(msgHash)), hexToBytes(addHexPrefix(this.pk)), {
11004
+ prehash: false,
11005
+ format: "recovered"
11006
+ }),
11007
+ "recovered"
10261
11008
  );
10262
11009
  return this.formatEthSignature(signature);
10263
11010
  }
@@ -10540,7 +11287,7 @@ function getLedgerPathBuffer111(accountId, applicationName = "LedgerW") {
10540
11287
  }
10541
11288
 
10542
11289
  // src/utils/address.ts
10543
- import { hexToBytes as hexToBytes2 } from "@noble/curves/abstract/utils";
11290
+ import { hexToBytes as hexToBytes2 } from "@noble/curves/utils.js";
10544
11291
  function addAddressPadding(address) {
10545
11292
  const hex = toHex(isString(address) ? addHexPrefix(address) : address);
10546
11293
  const padded = removeHexPrefix(hex).padStart(64, "0");
@@ -11651,7 +12398,7 @@ var Deployer = class {
11651
12398
  }
11652
12399
  return CallData.compile(constructorCalldata);
11653
12400
  });
11654
- const deploySalt = salt ?? randomAddress();
12401
+ const deploySalt = salt ?? randomFelt();
11655
12402
  return {
11656
12403
  call: {
11657
12404
  contractAddress: toHex(this.address),
@@ -12185,7 +12932,7 @@ var Account = class {
12185
12932
  * ```
12186
12933
  */
12187
12934
  async getSnip9Nonce() {
12188
- const nonce = randomAddress();
12935
+ const nonce = randomFelt();
12189
12936
  const isValidNonce = await this.isValidSnip9Nonce(nonce);
12190
12937
  if (!isValidNonce) {
12191
12938
  return this.getSnip9Nonce();
@@ -13852,6 +14599,7 @@ export {
13852
14599
  ETxnResponseFlag,
13853
14600
  EntryPointType,
13854
14601
  EthSigner,
14602
+ HttpTransport,
13855
14603
  Int,
13856
14604
  LedgerSigner111 as LedgerSigner,
13857
14605
  LedgerSigner111,
@@ -13870,11 +14618,12 @@ export {
13870
14618
  RpcProvider as Provider,
13871
14619
  ProviderInterface,
13872
14620
  api_exports as RPC,
13873
- rpc_0_10_2_exports as RPC0102,
13874
- rpc_0_10_3_exports as RPC0103,
13875
- rpc_0_9_0_exports as RPC09,
14621
+ v0_10_2_exports as RPC0102,
14622
+ v0_10_3_exports as RPC0103,
14623
+ v0_9_exports as RPC09,
13876
14624
  RPCResponseParser,
13877
14625
  ReceiptTx,
14626
+ ReconnectingWsTransport,
13878
14627
  ResponseParser,
13879
14628
  RpcChannel3 as RpcChannel,
13880
14629
  RpcError,
@@ -13905,6 +14654,8 @@ export {
13905
14654
  WalletAccountV6,
13906
14655
  WebSocketChannel,
13907
14656
  WebSocketNotConnectedError,
14657
+ WebSocketProvider,
14658
+ WsTransport,
13908
14659
  addAddressPadding,
13909
14660
  brotherId,
13910
14661
  byteArray_exports as byteArray,