@tronweb3/tronwallet-adapter-tokenpocket 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/umd/index.js CHANGED
@@ -519,13 +519,60 @@
519
519
  function isPlainObject(value) {
520
520
  return typeof value === 'object' && value !== null && !Array.isArray(value);
521
521
  }
522
+ /** Non-negative decimal integer, e.g. `1`, `728126428`. */
523
+ const DECIMAL_INTEGER = /^[0-9]+$/;
524
+ /** Non-negative hex integer, e.g. `0x2b6653dc`. `BigInt` accepts either prefix case. */
525
+ const HEX_INTEGER = /^0[xX][0-9a-fA-F]+$/;
526
+ /**
527
+ * Convert a `domain.chainId` to the `number` wallets expect, refusing anything
528
+ * that cannot survive the trip.
529
+ *
530
+ * EIP-712 declares `chainId` as `uint256`, but `Number()` maps that domain onto
531
+ * IEEE-754 doubles and quietly accepts values that are not integers at all:
532
+ * `Number('9007199254740993')` yields `9007199254740992`, and `'1.5'`, `'-1'`,
533
+ * `'1e3'` and `''` all pass `Number.isFinite()`. Each of those makes the wallet
534
+ * sign a domain that differs from what the caller supplied, so the signature no
535
+ * longer corresponds to the original payload. Parsing through `BigInt` keeps the
536
+ * value exact and lets the unrepresentable cases fail loudly instead.
537
+ */
538
+ function toChainIdNumber(chainId) {
539
+ // Reject anything that is not an integer literal before converting. `Number()`
540
+ // happily turns `'1.5'`, `'-1'`, `'1e3'` and `''` into finite numbers, none of
541
+ // which is a uint256.
542
+ if (typeof chainId === 'string') {
543
+ const value = chainId.trim();
544
+ if (!DECIMAL_INTEGER.test(value) && !HEX_INTEGER.test(value)) {
545
+ throw new errors_js_1.WalletSignTypedDataError(`Invalid domain.chainId: ${chainId}. Expected a non-negative decimal or 0x-prefixed hex integer.`);
546
+ }
547
+ }
548
+ const parsed = Number(chainId);
549
+ // Catches NaN, Infinity and fractions, which only a number/bigint input can
550
+ // still be at this point — the string form was already gated by the regexes.
551
+ if (!Number.isInteger(parsed)) {
552
+ throw new errors_js_1.WalletSignTypedDataError(`Invalid domain.chainId: ${chainId}. Expected a non-negative integer.`);
553
+ }
554
+ if (parsed < 0) {
555
+ throw new errors_js_1.WalletSignTypedDataError(`Invalid domain.chainId: ${chainId}. Expected a non-negative integer.`);
556
+ }
557
+ // With integrality established, `isSafeInteger` is an exact bound check rather
558
+ // than an approximation: every integer up to MAX_SAFE_INTEGER converts
559
+ // losslessly, and anything larger necessarily rounds to 2^53 or beyond, which is
560
+ // never "safe". So a value that survives this equals what the caller supplied.
561
+ if (!Number.isSafeInteger(parsed)) {
562
+ throw new errors_js_1.WalletSignTypedDataError(`domain.chainId ${chainId} exceeds Number.MAX_SAFE_INTEGER (${Number.MAX_SAFE_INTEGER}) and cannot be represented as a number without losing precision.`);
563
+ }
564
+ return parsed;
565
+ }
522
566
  /**
523
567
  * Validate a TypedData payload and return a normalized copy safe to pass to wallets.
524
568
  *
525
- * Current normalization: convert `domain.chainId` from string to number (some wallets,
526
- * e.g. TronLink, require a numeric chainId).
569
+ * Current normalization: convert `domain.chainId` to a number (some wallets,
570
+ * e.g. TronLink, require a numeric chainId). `null`/`undefined` are left alone
571
+ * because `chainId` is optional, as are values of other types — a `BigNumber`
572
+ * instance is a legitimate `BigNumberish` and is passed through untouched.
527
573
  *
528
- * Throws {@link WalletSignTypedDataError} if the payload is not a valid TypedData object.
574
+ * Throws {@link WalletSignTypedDataError} if the payload is not a valid TypedData
575
+ * object, or if `domain.chainId` cannot be represented exactly as a number.
529
576
  */
530
577
  function normalizeAndValidateTypedData(typedData) {
531
578
  if (!isPlainObject(typedData)) {
@@ -541,10 +588,10 @@
541
588
  throw new errors_js_1.WalletSignTypedDataError('typedData.message must be an object.');
542
589
  }
543
590
  const { chainId } = typedData.domain;
544
- if (typeof chainId === 'string') {
545
- const parsed = Number(chainId);
546
- if (!Number.isFinite(parsed)) {
547
- throw new errors_js_1.WalletSignTypedDataError(`Invalid domain.chainId: ${chainId}.`);
591
+ if (typeof chainId === 'string' || typeof chainId === 'number' || typeof chainId === 'bigint') {
592
+ const parsed = toChainIdNumber(chainId);
593
+ if (parsed === chainId) {
594
+ return typedData;
548
595
  }
549
596
  return Object.assign(Object.assign({}, typedData), { domain: Object.assign(Object.assign({}, typedData.domain), { chainId: parsed }) });
550
597
  }
@@ -685,58 +732,135 @@
685
732
  function requireUtils$2 () {
686
733
  if (hasRequiredUtils$2) return utils$2;
687
734
  hasRequiredUtils$2 = 1;
688
- Object.defineProperty(utils$2, "__esModule", { value: true });
689
- utils$2.isInBrowser = isInBrowser;
690
- utils$2.checkAdapterState = checkAdapterState;
691
- utils$2.isInMobileBrowser = isInMobileBrowser;
692
- /**
693
- * check simply if current environment is browser or not
694
- * @returns boolean
695
- */
696
- function isInBrowser() {
697
- return typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined';
698
- }
699
- /**
700
- *
701
- * @param {Function} check funcion to check if wallet is installed. return true if wallet is detected.
702
- * @returns
703
- */
704
- function checkAdapterState(check) {
705
- if (!isInBrowser())
706
- return;
707
- const disposers = [];
708
- function dispose() {
709
- for (const dispose of disposers) {
710
- dispose();
711
- }
712
- }
713
- function checkAndDispose() {
714
- if (check()) {
715
- dispose();
716
- }
717
- }
718
- const interval = setInterval(checkAndDispose, 500);
719
- disposers.push(() => clearInterval(interval));
720
- if (document.readyState === 'loading') {
721
- document.addEventListener('DOMContentLoaded', checkAndDispose, { once: true });
722
- disposers.push(() => document.removeEventListener('DOMContentLoaded', checkAndDispose));
723
- }
724
- if (document.readyState !== 'complete') {
725
- window.addEventListener('load', checkAndDispose, { once: true });
726
- disposers.push(() => window.removeEventListener('load', checkAndDispose));
727
- }
728
- checkAndDispose();
729
- // stop all task after 1min
730
- setTimeout(dispose, 60 * 1000);
731
- }
732
- /**
733
- * Simplily detect mobile device
734
- */
735
- function isInMobileBrowser() {
736
- return (typeof navigator !== 'undefined' &&
737
- navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i));
738
- }
739
-
735
+ (function (exports$1) {
736
+ Object.defineProperty(exports$1, "__esModule", { value: true });
737
+ exports$1.MAX_CHECK_TIMEOUT = void 0;
738
+ exports$1.validateCheckTimeout = validateCheckTimeout;
739
+ exports$1.omitUndefined = omitUndefined;
740
+ exports$1.assertConnectAddress = assertConnectAddress;
741
+ exports$1.isInBrowser = isInBrowser;
742
+ exports$1.checkAdapterState = checkAdapterState;
743
+ exports$1.isInMobileBrowser = isInMobileBrowser;
744
+ const errors_js_1 = requireErrors();
745
+ /**
746
+ * Upper bound for `config.checkTimeout`, in milliseconds.
747
+ *
748
+ * Wallet detection polls on a fixed interval and gives up after
749
+ * `checkTimeout / interval` ticks. An unbounded value makes that loop
750
+ * indistinguishable from one that never stops, so it is capped. Ten minutes is
751
+ * far beyond any realistic detection window.
752
+ */
753
+ exports$1.MAX_CHECK_TIMEOUT = 10 * 60 * 1000;
754
+ /**
755
+ * Validate `config.checkTimeout` and throw when it cannot produce a terminating
756
+ * detection loop.
757
+ *
758
+ * `typeof value === 'number'` is not enough on its own: `NaN` and `Infinity` are
759
+ * numbers, and adapters derive their polling bound from
760
+ * `Math.floor(checkTimeout / interval)`. With `NaN` that bound is `NaN`, so
761
+ * `times > maxTimes` is never true — the interval is never cleared and the
762
+ * detection promise never settles, hanging `connect()` for the life of the page.
763
+ * TypeScript cannot prevent this because adapters are also constructed from
764
+ * plain JavaScript, JSON config and `as any`.
765
+ *
766
+ * @param value the raw value taken from the adapter config
767
+ * @param prefix prefix used in the thrown message, e.g. `[TronLinkAdapter]`
768
+ */
769
+ function validateCheckTimeout(value, prefix = '[WalletAdapter]') {
770
+ if (typeof value !== 'number') {
771
+ throw new Error(`${prefix} config.checkTimeout should be a number`);
772
+ }
773
+ if (!Number.isFinite(value) || value < 0 || value > exports$1.MAX_CHECK_TIMEOUT) {
774
+ throw new Error(`${prefix} config.checkTimeout should be a finite number between 0 and ${exports$1.MAX_CHECK_TIMEOUT}, but got ${value}`);
775
+ }
776
+ }
777
+ /**
778
+ * Copy an object without the keys whose value is exactly `undefined`.
779
+ *
780
+ * Spreading a caller's config over a set of defaults is only safe when explicit
781
+ * `undefined`s have been dropped first: `{ ...defaults, ...{ checkTimeout: undefined } }`
782
+ * yields `checkTimeout: undefined`, which defeats the default and every validation
783
+ * that ran against it. Callers routinely produce such objects by spreading their own
784
+ * optional config, so adapters must sanitise before merging rather than trusting the
785
+ * `Required<...>` type on the resulting field.
786
+ *
787
+ * @param params the raw config object, possibly `undefined`
788
+ * @returns a shallow copy containing only the keys that were actually set
789
+ */
790
+ function omitUndefined(params) {
791
+ return Object.fromEntries(Object.entries(params !== null && params !== void 0 ? params : {}).filter(([, value]) => value !== undefined));
792
+ }
793
+ /**
794
+ * Guard the transition into `AdapterState.Connected`.
795
+ *
796
+ * A resolved account request is not the same thing as a connection. Wallets
797
+ * answer `tron_requestAccounts`/`eth_requestAccounts` with a success code while
798
+ * still returning an empty account list, and injected `tronWeb.defaultAddress`
799
+ * is frequently not populated yet at the moment the request resolves. Treating
800
+ * either as "connected" leaves the adapter reporting `connected === true` with
801
+ * no address, and emits `connect('')` to the dapp; every later signMessage or
802
+ * signTransaction then passes the state guard only to fail inside the provider.
803
+ *
804
+ * @param address the address the adapter is about to store. `false` is accepted
805
+ * because tronweb types `tronWeb.defaultAddress.base58` as `string | false`.
806
+ * @returns the same address, narrowed to a non-empty string
807
+ * @throws {WalletConnectionError} when no usable address was obtained
808
+ */
809
+ function assertConnectAddress(address) {
810
+ if (!address) {
811
+ throw new errors_js_1.WalletConnectionError('Request connect error.');
812
+ }
813
+ return address;
814
+ }
815
+ /**
816
+ * check simply if current environment is browser or not
817
+ * @returns boolean
818
+ */
819
+ function isInBrowser() {
820
+ return typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined';
821
+ }
822
+ /**
823
+ *
824
+ * @param {Function} check funcion to check if wallet is installed. return true if wallet is detected.
825
+ * @returns
826
+ */
827
+ function checkAdapterState(check) {
828
+ if (!isInBrowser())
829
+ return;
830
+ const disposers = [];
831
+ function dispose() {
832
+ for (const dispose of disposers) {
833
+ dispose();
834
+ }
835
+ }
836
+ function checkAndDispose() {
837
+ if (check()) {
838
+ dispose();
839
+ }
840
+ }
841
+ const interval = setInterval(checkAndDispose, 500);
842
+ disposers.push(() => clearInterval(interval));
843
+ if (document.readyState === 'loading') {
844
+ document.addEventListener('DOMContentLoaded', checkAndDispose, { once: true });
845
+ disposers.push(() => document.removeEventListener('DOMContentLoaded', checkAndDispose));
846
+ }
847
+ if (document.readyState !== 'complete') {
848
+ window.addEventListener('load', checkAndDispose, { once: true });
849
+ disposers.push(() => window.removeEventListener('load', checkAndDispose));
850
+ }
851
+ checkAndDispose();
852
+ // stop all task after 1min
853
+ setTimeout(dispose, 60 * 1000);
854
+ }
855
+ /**
856
+ * Simplily detect mobile device
857
+ */
858
+ function isInMobileBrowser() {
859
+ return (typeof navigator !== 'undefined' &&
860
+ navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i));
861
+ }
862
+
863
+ } (utils$2));
740
864
  return utils$2;
741
865
  }
742
866
 
@@ -761,6 +885,7 @@
761
885
  };
762
886
  Object.defineProperty(exports$1, "__esModule", { value: true });
763
887
  exports$1.defaultSecurityOptions = void 0;
888
+ exports$1.validateSecurityOptions = validateSecurityOptions;
764
889
  exports$1.clearCache = clearCache;
765
890
  exports$1.fetchJsonWithCache = fetchJsonWithCache;
766
891
  exports$1.defaultSecurityOptions = {
@@ -773,6 +898,83 @@
773
898
  retries: 0,
774
899
  cacheTTL: 10 * 60 * 1000,
775
900
  };
901
+ /** A config fetch that takes longer than this is never worth waiting for. */
902
+ const MAX_SECURITY_TIMEOUT = 60 * 1000;
903
+ /** `retries` multiplies the time `connect()` can block, so it is capped. */
904
+ const MAX_SECURITY_RETRIES = 10;
905
+ function assertFiniteNumber(value, field, min, max) {
906
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < min || value > max) {
907
+ throw new Error(`[WalletAdapter] config.securityOptions.${field} should be a finite number between ${min} and ${max}, but got ${String(value)}`);
908
+ }
909
+ }
910
+ function parseAbsoluteUrl(url) {
911
+ try {
912
+ return new URL(url);
913
+ }
914
+ catch (_a) {
915
+ // Not absolute — a relative path, which `fetch` resolves against the page.
916
+ return null;
917
+ }
918
+ }
919
+ function validateConfigUrls(configUrls) {
920
+ if (!Array.isArray(configUrls)) {
921
+ throw new Error(`[WalletAdapter] config.securityOptions.configUrls should be an array of URLs, but got ${typeof configUrls}`);
922
+ }
923
+ for (const url of configUrls) {
924
+ if (typeof url !== 'string' || url.trim() === '') {
925
+ throw new Error(`[WalletAdapter] config.securityOptions.configUrls should only contain non-empty URL strings, but got ${String(url)}`);
926
+ }
927
+ // Relative paths are legitimate — they resolve against the page and `new URL`
928
+ // rejects them — so only values that parse as an absolute URL are checked,
929
+ // and then only for the scheme `fetch` can actually retrieve.
930
+ const parsed = parseAbsoluteUrl(url);
931
+ if (parsed && parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
932
+ throw new Error(`[WalletAdapter] config.securityOptions.configUrls only supports http(s) URLs, but got ${url}`);
933
+ }
934
+ }
935
+ }
936
+ /**
937
+ * Validate `config.securityOptions` at construction time.
938
+ *
939
+ * Every field here is consumed much later, inside `connect()` — a `configUrls`
940
+ * string instead of an array only fails when `fetchJsonWithCache` calls `.map`
941
+ * on it, and a `NaN` timeout or a negative `retries` silently changes the fetch
942
+ * behaviour rather than reporting anything. Failing fast in the constructor puts
943
+ * the error where the caller can act on it.
944
+ */
945
+ function validateSecurityOptions(securityOptions) {
946
+ const { enabled, configUrls, timeout, retries, cacheTTL, onRiskDetected, onConfigFallback } = securityOptions;
947
+ // `enabled` is read as a plain truthy value in `checkSecurity()` and again just
948
+ // below, so a non-boolean silently resolves to the opposite of what it reads like:
949
+ // `'false'` is truthy and turns the check on. Check it before the `configUrls`
950
+ // requirement below, or `{ enabled: 'false' }` reports a missing `configUrls`
951
+ // "when enabled is true" — for a caller who wrote `false`.
952
+ if (enabled !== undefined && typeof enabled !== 'boolean') {
953
+ throw new Error(`[WalletAdapter] config.securityOptions.enabled should be a boolean, but got ${typeof enabled}`);
954
+ }
955
+ if (configUrls !== undefined) {
956
+ validateConfigUrls(configUrls);
957
+ }
958
+ if (enabled && (!configUrls || configUrls.length === 0)) {
959
+ throw new Error(`[WalletAdapter] config.securityOptions.configUrls is required when securityOptions.enabled is true`);
960
+ }
961
+ if (timeout !== undefined) {
962
+ assertFiniteNumber(timeout, 'timeout', 1, MAX_SECURITY_TIMEOUT);
963
+ }
964
+ if (cacheTTL !== undefined) {
965
+ // No upper bound: caching the risk config for a long time is a valid choice.
966
+ assertFiniteNumber(cacheTTL, 'cacheTTL', 0, Number.MAX_SAFE_INTEGER);
967
+ }
968
+ if (retries !== undefined && (!Number.isInteger(retries) || retries < 0 || retries > MAX_SECURITY_RETRIES)) {
969
+ throw new Error(`[WalletAdapter] config.securityOptions.retries should be an integer between 0 and ${MAX_SECURITY_RETRIES}, but got ${String(retries)}`);
970
+ }
971
+ if (onRiskDetected !== undefined && typeof onRiskDetected !== 'function') {
972
+ throw new Error(`[WalletAdapter] config.securityOptions.onRiskDetected should be a function, but got ${typeof onRiskDetected}`);
973
+ }
974
+ if (onConfigFallback !== undefined && typeof onConfigFallback !== 'function') {
975
+ throw new Error(`[WalletAdapter] config.securityOptions.onConfigFallback should be a function, but got ${typeof onConfigFallback}`);
976
+ }
977
+ }
776
978
  function isValidRisk(risk) {
777
979
  return (!!risk &&
778
980
  typeof risk === 'object' &&
@@ -944,18 +1146,16 @@
944
1146
  openUrlWhenWalletNotFound: true,
945
1147
  securityOptions: security_js_1.defaultSecurityOptions,
946
1148
  };
1149
+ this._connectPromise = null;
947
1150
  this._securityCheckCache = null;
948
- this.commonConfig = Object.assign(Object.assign({}, this.commonConfig), params);
949
- if (typeof this.commonConfig.checkTimeout !== 'number') {
950
- throw new Error(`[WalletAdapter] config.checkTimeout should be a number`);
951
- }
952
- AddonAdapter._validateSecurityOptions(this.commonConfig.securityOptions);
953
- }
954
- static _validateSecurityOptions(securityOptions) {
955
- const { enabled, configUrls } = securityOptions;
956
- if (enabled && (!configUrls || configUrls.length === 0)) {
957
- throw new Error(`[WalletAdapter] config.securityOptions.configUrls is required when securityOptions.enabled is true`);
958
- }
1151
+ // Drop explicit `undefined`s before merging: spreading them would overwrite
1152
+ // the defaults with `undefined` and make `Required<BaseAdapterConfig>` a lie,
1153
+ // which matters because callers routinely spread optional config objects.
1154
+ // Subclasses that re-merge their own config on top of `commonConfig` must
1155
+ // sanitise it the same way — see `omitUndefined`.
1156
+ this.commonConfig = Object.assign(Object.assign({}, this.commonConfig), (0, utils_js_1.omitUndefined)(params));
1157
+ (0, utils_js_1.validateCheckTimeout)(this.commonConfig.checkTimeout);
1158
+ (0, security_js_1.validateSecurityOptions)(this.commonConfig.securityOptions);
959
1159
  }
960
1160
  /**
961
1161
  * Update the security options at runtime. The given options are merged into
@@ -964,10 +1164,41 @@
964
1164
  */
965
1165
  updateSecurityOptions(securityOptions) {
966
1166
  const merged = Object.assign(Object.assign({}, this.commonConfig.securityOptions), securityOptions);
967
- AddonAdapter._validateSecurityOptions(merged);
1167
+ (0, security_js_1.validateSecurityOptions)(merged);
968
1168
  this.commonConfig.securityOptions = merged;
969
1169
  this._securityCheckCache = null;
970
1170
  }
1171
+ /**
1172
+ * Serialise `connect()` so that concurrent callers cannot each reach the wallet.
1173
+ *
1174
+ * The `connecting` flag alone cannot do this. Subclasses only raise it *after*
1175
+ * `await this._beforeConnect()` resolves, and `_beforeConnect()` itself awaits
1176
+ * wallet discovery and the security check — so two calls made in the same tick
1177
+ * both pass the guard while it is still `false`, and both go on to call
1178
+ * `tron_requestAccounts`. That produces a second authorisation popup, a `4000`
1179
+ * "pending request" error from the provider, and duplicated connect events.
1180
+ *
1181
+ * Claiming the in-flight promise here is synchronous — there is no `await`
1182
+ * between the check and the assignment — so a second caller always observes it
1183
+ * and simply shares the first call's result. Because the whole subclass
1184
+ * `_connect()` runs inside it, discovery, the security check and the provider
1185
+ * request are all in the same critical section.
1186
+ */
1187
+ connect(options) {
1188
+ if (this._connectPromise) {
1189
+ return this._connectPromise;
1190
+ }
1191
+ // Deliberately not `async`: that would wrap the shared promise in a fresh one
1192
+ // per caller. The IIFE still turns a synchronous throw from `_connect()` into
1193
+ // a rejection, so callers never see one thrown at them directly.
1194
+ const promise = (() => __awaiter(this, void 0, void 0, function* () { return this._connect(options); }))().finally(() => {
1195
+ if (this._connectPromise === promise) {
1196
+ this._connectPromise = null;
1197
+ }
1198
+ });
1199
+ this._connectPromise = promise;
1200
+ return promise;
1201
+ }
971
1202
  /**
972
1203
  * Run the pre-connect pipeline (wallet discovery, deeplink/url fallback,
973
1204
  * security check).
@@ -1238,8 +1469,8 @@
1238
1469
  this._updateWallet = () => __awaiter(this, void 0, void 0, function* () {
1239
1470
  var _a, _b, _c, _d, _e, _f, _g, _h;
1240
1471
  this._supportNewTronProtocol = false;
1241
- let state = this.state;
1242
- let address = this.address;
1472
+ let state;
1473
+ let address;
1243
1474
  if ((0, tronwallet_abstract_adapter_1.isInMobileBrowser)()) {
1244
1475
  if (window.tronLink) {
1245
1476
  this._wallet = window.tronLink;
@@ -1278,8 +1509,6 @@
1278
1509
  }
1279
1510
  catch (e) {
1280
1511
  console.error('Unknown error: ' + e, ' Please install TronLink extension wallet.');
1281
- address = null;
1282
- state = tronwallet_abstract_adapter_1.AdapterState.Disconnect;
1283
1512
  this._readyState = tronwallet_abstract_adapter_1.WalletReadyState.NotFound;
1284
1513
  this.emit('readyStateChanged', this.readyState);
1285
1514
  return;
@@ -1384,7 +1613,7 @@
1384
1613
  }
1385
1614
  });
1386
1615
  }
1387
- connect() {
1616
+ _connect() {
1388
1617
  return __awaiter(this, void 0, void 0, function* () {
1389
1618
  var _a, _b;
1390
1619
  try {
@@ -1398,7 +1627,7 @@
1398
1627
  const wallet = this._wallet;
1399
1628
  try {
1400
1629
  const res = yield wallet.request({ method: 'eth_requestAccounts' });
1401
- const address = res[0];
1630
+ const address = (0, tronwallet_abstract_adapter_1.assertConnectAddress)(res === null || res === void 0 ? void 0 : res[0]);
1402
1631
  this.setAddress(address);
1403
1632
  this.setState(tronwallet_abstract_adapter_1.AdapterState.Connected);
1404
1633
  this._listenTronEvent();
@@ -1432,13 +1661,13 @@
1432
1661
  if (res.code === 4001) {
1433
1662
  throw new tronwallet_abstract_adapter_1.WalletConnectionError('The user rejected connection.');
1434
1663
  }
1435
- const address = ((_a = wallet.tronWeb.defaultAddress) === null || _a === void 0 ? void 0 : _a.base58) || '';
1664
+ const address = (0, tronwallet_abstract_adapter_1.assertConnectAddress)((_a = wallet.tronWeb.defaultAddress) === null || _a === void 0 ? void 0 : _a.base58);
1436
1665
  this.setAddress(address);
1437
1666
  this.setState(tronwallet_abstract_adapter_1.AdapterState.Connected);
1438
1667
  }
1439
1668
  else if (window.tronWeb) {
1440
1669
  const wallet = this._wallet;
1441
- const address = ((_b = wallet.tronWeb.defaultAddress) === null || _b === void 0 ? void 0 : _b.base58) || '';
1670
+ const address = (0, tronwallet_abstract_adapter_1.assertConnectAddress)((_b = wallet.tronWeb.defaultAddress) === null || _b === void 0 ? void 0 : _b.base58);
1442
1671
  this.setAddress(address);
1443
1672
  this.setState(tronwallet_abstract_adapter_1.AdapterState.Connected);
1444
1673
  }
@@ -1615,7 +1844,7 @@
1615
1844
  }
1616
1845
  if ((0, tronwallet_abstract_adapter_1.isInBrowser)() && !(0, tronwallet_abstract_adapter_1.isInMobileBrowser)()) {
1617
1846
  // Desktop: use TIP-6963 event-based discovery with fallback
1618
- this._checkPromise = new Promise((resolve) => {
1847
+ const detection = new Promise((resolve) => {
1619
1848
  let handled = false;
1620
1849
  let timer = null;
1621
1850
  let handler = null;
@@ -1682,13 +1911,23 @@
1682
1911
  window.addEventListener(tronwallet_abstract_adapter_1.TIP6963AnnounceProviderEventName, handler);
1683
1912
  window.dispatchEvent(new Event(tronwallet_abstract_adapter_1.TIP6963RequestProviderEventName));
1684
1913
  });
1685
- return this._checkPromise;
1914
+ this._checkPromise = detection;
1915
+ // Never cache a failed detection: the wallet may inject late or be enabled at
1916
+ // runtime. Unlike the polling adapters this keeps the full detection window on a
1917
+ // retry, because the TIP-6963 announce is asynchronous and a shortened window
1918
+ // would race it.
1919
+ void detection.then((found) => {
1920
+ if (!found && this._checkPromise === detection) {
1921
+ this._checkPromise = null;
1922
+ }
1923
+ });
1924
+ return detection;
1686
1925
  }
1687
1926
  // Mobile: use legacy polling detection
1688
1927
  const interval = 100;
1689
1928
  const maxTimes = Math.floor(this.config.checkTimeout / interval);
1690
1929
  let times = 0, timer;
1691
- this._checkPromise = new Promise((resolve) => {
1930
+ const detection = new Promise((resolve) => {
1692
1931
  const check = () => {
1693
1932
  times++;
1694
1933
  this._updateWallet();
@@ -1703,7 +1942,17 @@
1703
1942
  timer = setInterval(check, interval);
1704
1943
  check();
1705
1944
  });
1706
- return this._checkPromise;
1945
+ this._checkPromise = detection;
1946
+ // Never cache a failed detection: the wallet may inject late or be enabled at
1947
+ // runtime. Unlike the polling adapters this keeps the full detection window on a
1948
+ // retry, because the TIP-6963 announce is asynchronous and a shortened window
1949
+ // would race it.
1950
+ void detection.then((found) => {
1951
+ if (!found && this._checkPromise === detection) {
1952
+ this._checkPromise = null;
1953
+ }
1954
+ });
1955
+ return detection;
1707
1956
  }
1708
1957
  checkForWalletReadyForApp() {
1709
1958
  if (this.checkReadyInterval) {
@@ -1895,8 +2144,8 @@
1895
2144
  this._checkPromise = null;
1896
2145
  this._updateWallet = () => __awaiter(this, void 0, void 0, function* () {
1897
2146
  var _a, _b, _c, _d, _e;
1898
- let state = this.state;
1899
- let address = this.address;
2147
+ let state;
2148
+ let address;
1900
2149
  if ((0, utils_js_1.supportTokenPocket)()) {
1901
2150
  try {
1902
2151
  yield this.checkSecurity();
@@ -1993,7 +2242,7 @@
1993
2242
  }
1994
2243
  });
1995
2244
  }
1996
- connect() {
2245
+ _connect() {
1997
2246
  return __awaiter(this, void 0, void 0, function* () {
1998
2247
  try {
1999
2248
  if (!(yield this._beforeConnect()))
@@ -2199,7 +2448,7 @@
2199
2448
  return this._checkPromise;
2200
2449
  }
2201
2450
  if ((0, tronwallet_abstract_adapter_1.isInBrowser)() && !(0, tronwallet_abstract_adapter_1.isInMobileBrowser)()) {
2202
- this._checkPromise = new Promise((resolve) => {
2451
+ const detection = new Promise((resolve) => {
2203
2452
  const timer = setTimeout(() => {
2204
2453
  window.removeEventListener(tronwallet_abstract_adapter_2.TIP6963AnnounceProviderEventName, handler);
2205
2454
  this._updateWallet();
@@ -2253,13 +2502,23 @@
2253
2502
  window.addEventListener(tronwallet_abstract_adapter_2.TIP6963AnnounceProviderEventName, handler);
2254
2503
  window.dispatchEvent(new Event(tronwallet_abstract_adapter_2.TIP6963RequestProviderEventName));
2255
2504
  });
2256
- return this._checkPromise;
2505
+ this._checkPromise = detection;
2506
+ // Never cache a failed detection: the wallet may inject late or be enabled at
2507
+ // runtime. Unlike the polling adapters this keeps the full detection window on a
2508
+ // retry, because the TIP-6963 announce is asynchronous and a shortened window
2509
+ // would race it.
2510
+ void detection.then((found) => {
2511
+ if (!found && this._checkPromise === detection) {
2512
+ this._checkPromise = null;
2513
+ }
2514
+ });
2515
+ return detection;
2257
2516
  }
2258
2517
  // Support TIP-6963 with wallet extension
2259
2518
  const interval = 100;
2260
2519
  const maxTimes = Math.floor(this.config.checkTimeout / interval);
2261
2520
  let times = 0, timer;
2262
- this._checkPromise = new Promise((resolve) => {
2521
+ const detection = new Promise((resolve) => {
2263
2522
  const check = () => {
2264
2523
  times++;
2265
2524
  const isSupport = (0, utils_js_1.supportTokenPocket)();
@@ -2274,7 +2533,17 @@
2274
2533
  timer = setInterval(check, interval);
2275
2534
  check();
2276
2535
  });
2277
- return this._checkPromise;
2536
+ this._checkPromise = detection;
2537
+ // Never cache a failed detection: the wallet may inject late or be enabled at
2538
+ // runtime. Unlike the polling adapters this keeps the full detection window on a
2539
+ // retry, because the TIP-6963 announce is asynchronous and a shortened window
2540
+ // would race it.
2541
+ void detection.then((found) => {
2542
+ if (!found && this._checkPromise === detection) {
2543
+ this._checkPromise = null;
2544
+ }
2545
+ });
2546
+ return detection;
2278
2547
  }
2279
2548
  setAddress(address) {
2280
2549
  this._address = address;