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/CHANGELOG.md +23 -0
- package/dist/index.d.ts +2808 -2063
- package/dist/index.global.js +5243 -4003
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +2580 -1825
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2574 -1823
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
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/
|
|
613
|
-
var
|
|
614
|
-
__export(
|
|
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/
|
|
739
|
-
var
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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
|
-
*
|
|
797
|
-
* @
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
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
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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/
|
|
850
|
-
import { sha256 } from "@noble/hashes/
|
|
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/
|
|
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/
|
|
1082
|
-
var
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
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
|
|
1095
|
-
|
|
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
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
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/
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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/
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
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
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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/
|
|
4753
|
-
var
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
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
|
|
4820
|
-
|
|
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
|
|
4823
|
-
return (
|
|
5006
|
+
function encodeBuiltins(builtins) {
|
|
5007
|
+
return builtins.map((it) => BigInt(encodeShortString(it)));
|
|
4824
5008
|
}
|
|
4825
|
-
function
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
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
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
const
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
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(
|
|
5029
|
+
return toHex(BigInt(hash) % ADDR_BOUND);
|
|
4846
5030
|
}
|
|
4847
|
-
function
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
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
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
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
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
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
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
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/
|
|
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 {
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
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
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
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
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
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
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
const
|
|
5664
|
-
|
|
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
|
-
|
|
5667
|
-
|
|
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 (
|
|
5670
|
-
|
|
5671
|
-
|
|
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
|
-
|
|
5675
|
-
const baseName = sierraFile.replace(/\.sierra\.json$/, "").replace(/\.json$/, "");
|
|
5676
|
-
casmFile = files.find((f) => f === `${baseName}.casm`);
|
|
6360
|
+
return status;
|
|
5677
6361
|
}
|
|
5678
|
-
|
|
5679
|
-
|
|
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
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
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
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
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
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
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
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
6423
|
+
var HttpTransport = class {
|
|
6424
|
+
options;
|
|
6425
|
+
constructor(options) {
|
|
6426
|
+
this.options = options;
|
|
5706
6427
|
}
|
|
5707
|
-
|
|
5708
|
-
|
|
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
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
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
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
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
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
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
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
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
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
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
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
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
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
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
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
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
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
}
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
}
|
|
5807
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
6619
|
+
allocateRequestId() {
|
|
6620
|
+
const id = this.wireId;
|
|
6621
|
+
this.wireId += 1;
|
|
6622
|
+
return id;
|
|
6623
|
+
}
|
|
5887
6624
|
/**
|
|
5888
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
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
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
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
|
-
*
|
|
5919
|
-
*
|
|
5920
|
-
*
|
|
5921
|
-
*
|
|
5922
|
-
*
|
|
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
|
-
|
|
5925
|
-
this.
|
|
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
|
-
|
|
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
|
-
*
|
|
5930
|
-
*
|
|
5931
|
-
*
|
|
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
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
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
|
-
|
|
5941
|
-
|
|
6835
|
+
this.processRequestQueue();
|
|
6836
|
+
}
|
|
6837
|
+
onClosed() {
|
|
6838
|
+
if (this.userInitiatedClose || !this.autoReconnect) {
|
|
6839
|
+
this.setState("closed");
|
|
6840
|
+
return;
|
|
5942
6841
|
}
|
|
5943
|
-
|
|
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
|
-
*
|
|
5948
|
-
*
|
|
5949
|
-
*
|
|
5950
|
-
*
|
|
5951
|
-
*
|
|
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
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
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
|
-
|
|
5959
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
5965
|
-
* @
|
|
5966
|
-
*
|
|
5967
|
-
*
|
|
5968
|
-
*
|
|
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
|
-
|
|
5974
|
-
this.
|
|
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
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
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,
|
|
7258
|
+
({ error, result } = await this.batchClient.fetch(method, params, id));
|
|
6148
7259
|
} else {
|
|
6149
|
-
const
|
|
6150
|
-
|
|
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/
|
|
6602
|
-
var
|
|
6603
|
-
|
|
6604
|
-
|
|
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
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
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,
|
|
7849
|
+
({ error, result } = await this.batchClient.fetch(method, params, id));
|
|
6709
7850
|
} else {
|
|
6710
|
-
const
|
|
6711
|
-
|
|
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/
|
|
7225
|
-
var
|
|
7226
|
-
__export(
|
|
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/
|
|
7235
|
-
var
|
|
7236
|
-
|
|
7237
|
-
|
|
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
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
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
|
-
*
|
|
7444
|
-
*
|
|
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
|
-
|
|
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.
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
this.
|
|
7465
|
-
this.
|
|
7466
|
-
this.
|
|
7467
|
-
this.
|
|
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.
|
|
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
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
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.
|
|
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
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7656
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
7892
|
-
|
|
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
|
-
|
|
7913
|
-
|
|
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
|
-
|
|
7941
|
-
|
|
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
|
-
|
|
7963
|
-
|
|
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
|
-
|
|
7985
|
-
|
|
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.
|
|
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
|
|
10332
|
+
this.channel = new v0_9_exports.RpcChannel({ ...options, waitMode: false });
|
|
9684
10333
|
} else if (isVersion("0.10", options.specVersion)) {
|
|
9685
|
-
this.channel = new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
10653
|
+
if (this.channel instanceof v0_10_2_exports.RpcChannel) {
|
|
10005
10654
|
return this.channel.getEvents(eventFilter);
|
|
10006
10655
|
}
|
|
10007
|
-
if (this.channel instanceof
|
|
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.
|
|
10196
|
-
|
|
10197
|
-
|
|
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.
|
|
10218
|
-
|
|
10219
|
-
|
|
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.
|
|
10240
|
-
|
|
10241
|
-
|
|
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.
|
|
10259
|
-
|
|
10260
|
-
|
|
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/
|
|
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 ??
|
|
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 =
|
|
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
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
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,
|