@dynamic-labs/sdk-react-core 4.88.5 → 4.89.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/CHANGELOG.md CHANGED
@@ -1,4 +1,27 @@
1
1
 
2
+ ## [4.89.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.88.6...v4.89.0) (2026-06-16)
3
+
4
+
5
+ ### Features
6
+
7
+ * **moonpay:** add useMoonPayOnramp hook ([#11405](https://github.com/dynamic-labs/dynamic-auth/issues/11405)) ([48cb62f](https://github.com/dynamic-labs/dynamic-auth/commit/48cb62fefecb511fcba9359ee6f6096dfef0b125))
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * get wallet metadata from wallet-book when its present ([#11566](https://github.com/dynamic-labs/dynamic-auth/issues/11566)) ([a1a8ad5](https://github.com/dynamic-labs/dynamic-auth/commit/a1a8ad53c063157b189aea138fa338fcc67dc4dd))
13
+ * **waas:** instant logout — clear key share host-side, background iframe cleanup ([#11583](https://github.com/dynamic-labs/dynamic-auth/issues/11583)) ([0ab3378](https://github.com/dynamic-labs/dynamic-auth/commit/0ab3378efdbbe233069b17fad62b0c126397bc3a))
14
+ * **wagmi-connector:** await disconnect before connect to defuse SyncDynamicWagmi race DYNT-549 ([#11579](https://github.com/dynamic-labs/dynamic-auth/issues/11579)) ([6ac7c0e](https://github.com/dynamic-labs/dynamic-auth/commit/6ac7c0e02ed863342047bc5a1e60be3a66b8a425)), closes [#11131](https://github.com/dynamic-labs/dynamic-auth/issues/11131) [#11513](https://github.com/dynamic-labs/dynamic-auth/issues/11513) [#11516](https://github.com/dynamic-labs/dynamic-auth/issues/11516) [#11496](https://github.com/dynamic-labs/dynamic-auth/issues/11496) [#11513](https://github.com/dynamic-labs/dynamic-auth/issues/11513) [/github.com/dynamic-labs/dynamic-auth/blob/main/packages/wagmi-connector/src/lib/hooks/useConnectorId/useConnectorId.ts#L19-L26](https://github.com/dynamic-labs//github.com/dynamic-labs/dynamic-auth/blob/main/packages/wagmi-connector/src/lib/hooks/useConnectorId/useConnectorId.ts/issues/L19-L26) [#11131](https://github.com/dynamic-labs/dynamic-auth/issues/11131) [#11516](https://github.com/dynamic-labs/dynamic-auth/issues/11516) [#11513](https://github.com/dynamic-labs/dynamic-auth/issues/11513)
15
+ * **react-native:** resolve intermittent "Wallet with id <uuid> not found" after login ([#11575](https://github.com/dynamic-labs/dynamic-auth/issues/11575)) ([9d7a246](https://github.com/dynamic-labs/dynamic-auth/commit/9d7a246167927b9339dc33c10977c6d12783ab88))
16
+
17
+ ### [4.88.6](https://github.com/dynamic-labs/dynamic-auth/compare/v4.88.5...v4.88.6) (2026-06-13)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **DynamicBridgeWidget:** prevent bridge chain sync from reverting manual wallet switches ([#11560](https://github.com/dynamic-labs/dynamic-auth/issues/11560)) ([40c4973](https://github.com/dynamic-labs/dynamic-auth/commit/40c4973761b3e887577664f72f89d1d6ba285ea3))
23
+ * resolve EOA wallet client on the correct chain for EIP-7702 smart wallets ([#11564](https://github.com/dynamic-labs/dynamic-auth/issues/11564)) ([893b083](https://github.com/dynamic-labs/dynamic-auth/commit/893b0837a450ec683ccd09f2bb54204b77b6bd50))
24
+
2
25
  ### [4.88.5](https://github.com/dynamic-labs/dynamic-auth/compare/v4.88.4...v4.88.5) (2026-06-11)
3
26
 
4
27
 
@@ -6502,4 +6525,4 @@ Enjoy!
6502
6525
  ### Bug Fixes
6503
6526
 
6504
6527
  * **view-context:** refactor view context to have initial view ([#2268](https://github.com/dynamic-labs/DynamicAuth/issues/2268)) ([0afe6ae](https://github.com/dynamic-labs/DynamicAuth/commit/0afe6ae469f62fd16fd8471322f9295957f607f6))
6505
- * **wcv2:** upgrade universal p
6528
+ * **wcv2:** upgrade universal p
package/package.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.88.5";
6
+ var version = "4.89.0";
7
7
  var dependencies = {
8
8
  "@dynamic-labs/sdk-api-core": "0.0.1015",
9
9
  "@dynamic-labs-sdk/client": "1.8.2",
package/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  'use client'
2
- var version = "4.88.5";
2
+ var version = "4.89.0";
3
3
  var dependencies = {
4
4
  "@dynamic-labs/sdk-api-core": "0.0.1015",
5
5
  "@dynamic-labs-sdk/client": "1.8.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/sdk-react-core",
3
- "version": "4.88.5",
3
+ "version": "4.89.0",
4
4
  "dependencies": {
5
5
  "@dynamic-labs/sdk-api-core": "0.0.1015",
6
6
  "@dynamic-labs-sdk/client": "1.8.2",
@@ -17,17 +17,17 @@
17
17
  "yup": "0.32.11",
18
18
  "react-international-phone": "4.5.0",
19
19
  "bs58": "5.0.0",
20
- "@dynamic-labs/assert-package-version": "4.88.5",
21
- "@dynamic-labs/iconic": "4.88.5",
22
- "@dynamic-labs/locale": "4.88.5",
23
- "@dynamic-labs/logger": "4.88.5",
24
- "@dynamic-labs/multi-wallet": "4.88.5",
25
- "@dynamic-labs/rpc-providers": "4.88.5",
26
- "@dynamic-labs/store": "4.88.5",
27
- "@dynamic-labs/types": "4.88.5",
28
- "@dynamic-labs/utils": "4.88.5",
29
- "@dynamic-labs/wallet-book": "4.88.5",
30
- "@dynamic-labs/wallet-connector-core": "4.88.5",
20
+ "@dynamic-labs/assert-package-version": "4.89.0",
21
+ "@dynamic-labs/iconic": "4.89.0",
22
+ "@dynamic-labs/locale": "4.89.0",
23
+ "@dynamic-labs/logger": "4.89.0",
24
+ "@dynamic-labs/multi-wallet": "4.89.0",
25
+ "@dynamic-labs/rpc-providers": "4.89.0",
26
+ "@dynamic-labs/store": "4.89.0",
27
+ "@dynamic-labs/types": "4.89.0",
28
+ "@dynamic-labs/utils": "4.89.0",
29
+ "@dynamic-labs/wallet-book": "4.89.0",
30
+ "@dynamic-labs/wallet-connector-core": "4.89.0",
31
31
  "eventemitter3": "5.0.1"
32
32
  },
33
33
  "devDependencies": {
@@ -119,7 +119,7 @@ export interface DynamicContextProps {
119
119
  /**
120
120
  * If true, the Metamask SDK will be used to connect to the wallet.
121
121
  * If false, the injected eip6963 provider will be used and WalletConnect for mobile.
122
- * @default true
122
+ * @default false
123
123
  */
124
124
  useMetamaskSdk?: boolean;
125
125
  };
@@ -5,6 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var React = require('react');
8
+ var utils = require('@dynamic-labs/utils');
8
9
  var createProviderHook = require('../../shared/utils/functions/createProviderHook/createProviderHook.cjs');
9
10
  var getWalletGroupType = require('../../utils/functions/getWalletGroupType/getWalletGroupType.cjs');
10
11
  var useWalletItemActions = require('../../utils/hooks/useWalletItemActions/useWalletItemActions.cjs');
@@ -22,6 +23,12 @@ const WalletGroupContextProvider = ({ children, }) => {
22
23
  const { handleWalletItemClick } = useWalletItemActions.useWalletItemActions();
23
24
  const navigateToWalletGroup = React__default["default"].useCallback((walletGroup, { onSelectWallet } = {}) => {
24
25
  setSelectedWalletGroup(walletGroup);
26
+ // On mobile the walletconnect group (EVM + SOL root connectors) should
27
+ // go straight to the WC wallet picker, not a chain-selection step.
28
+ if (utils.isMobile() && walletGroup.key === 'walletconnect') {
29
+ pushView('wallet-connect-mobile-wallets-list');
30
+ return;
31
+ }
25
32
  const walletGroupType = getWalletGroupType.getWalletGroupType(walletGroup);
26
33
  const mapGroupTypeToView = {
27
34
  'multi-chain': 'wallet-group',
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import React__default from 'react';
4
+ import { isMobile } from '@dynamic-labs/utils';
4
5
  import { createProviderHook } from '../../shared/utils/functions/createProviderHook/createProviderHook.js';
5
6
  import { getWalletGroupType } from '../../utils/functions/getWalletGroupType/getWalletGroupType.js';
6
7
  import { useWalletItemActions } from '../../utils/hooks/useWalletItemActions/useWalletItemActions.js';
@@ -14,6 +15,12 @@ const WalletGroupContextProvider = ({ children, }) => {
14
15
  const { handleWalletItemClick } = useWalletItemActions();
15
16
  const navigateToWalletGroup = React__default.useCallback((walletGroup, { onSelectWallet } = {}) => {
16
17
  setSelectedWalletGroup(walletGroup);
18
+ // On mobile the walletconnect group (EVM + SOL root connectors) should
19
+ // go straight to the WC wallet picker, not a chain-selection step.
20
+ if (isMobile() && walletGroup.key === 'walletconnect') {
21
+ pushView('wallet-connect-mobile-wallets-list');
22
+ return;
23
+ }
17
24
  const walletGroupType = getWalletGroupType(walletGroup);
18
25
  const mapGroupTypeToView = {
19
26
  'multi-chain': 'wallet-group',
@@ -23,7 +23,7 @@ const defaultDynamicSettings = {
23
23
  shadowDOMEnabled: true,
24
24
  showLockedWalletView: false,
25
25
  tokensFilter: (tokens) => tokens,
26
- useMetamaskSdk: true,
26
+ useMetamaskSdk: false,
27
27
  };
28
28
 
29
29
  exports.PUBLIC_PROJECT_LIVE_ENVIRONMENT_ID = PUBLIC_PROJECT_LIVE_ENVIRONMENT_ID;
@@ -16,5 +16,5 @@ export declare const defaultDynamicSettings: {
16
16
  shadowDOMEnabled: true;
17
17
  showLockedWalletView: false;
18
18
  tokensFilter: (tokens: import("@dynamic-labs/sdk-api-core").TokenBalance[]) => import("@dynamic-labs/sdk-api-core").TokenBalance[];
19
- useMetamaskSdk: true;
19
+ useMetamaskSdk: false;
20
20
  };
@@ -19,7 +19,7 @@ const defaultDynamicSettings = {
19
19
  shadowDOMEnabled: true,
20
20
  showLockedWalletView: false,
21
21
  tokensFilter: (tokens) => tokens,
22
- useMetamaskSdk: true,
22
+ useMetamaskSdk: false,
23
23
  };
24
24
 
25
25
  export { PUBLIC_PROJECT_LIVE_ENVIRONMENT_ID, defaultDynamicSettings };
@@ -13,7 +13,6 @@ var MockContext = require('../../../../context/MockContext/MockContext.cjs');
13
13
  var ViewContext = require('../../../../context/ViewContext/ViewContext.cjs');
14
14
  var dynamicEvents = require('../../../../events/dynamicEvents.cjs');
15
15
  var logger = require('../../../../shared/logger.cjs');
16
- var authMode = require('../../../../store/state/authMode/authMode.cjs');
17
16
  var localStorage = require('../../../constants/localStorage.cjs');
18
17
  require('@dynamic-labs/sdk-api-core');
19
18
  require('../../../constants/values.cjs');
@@ -41,8 +40,8 @@ var isSelectedWalletAlreadyConnected = require('../../../functions/isSelectedWal
41
40
  const useHandleWalletItem = ({ allowAlreadyConnectedWallet, onQrCodeConnect, onConnectOnly, onCustodialWallet, onInstalledExtension, }) => {
42
41
  const { setMultiWalletWidgetState, setShowAuthFlow, setSelectedWalletConnectorKey, walletConnectorOptions, setQrcodeUri, setDesktopUri, user, linkedWallets, setLegacyIsVerifying, } = useInternalDynamicContext.useInternalDynamicContext();
43
42
  const { mockedSDK } = MockContext.useMockContext();
44
- const { setError, setErrorMessage } = ErrorContext.useErrorContext();
45
- const { pushView, clearStackAndPushInitialView, replaceView } = ViewContext.useViewContext();
43
+ const { setError } = ErrorContext.useErrorContext();
44
+ const { pushView, replaceView } = ViewContext.useViewContext();
46
45
  const handleCustodialWalletClick = (walletConnector) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
47
46
  logger.logger.debug('handleCustodialWalletClick', walletConnector);
48
47
  const twoStepAuthentication = requiresTwoStepAuthentication.requiresTwoStepAuthentication(walletConnector);
@@ -189,31 +188,16 @@ const useHandleWalletItem = ({ allowAlreadyConnectedWallet, onQrCodeConnect, onC
189
188
  yield walletConnector.endSession();
190
189
  }
191
190
  setLegacyIsVerifying(false);
192
- const isMetamask = walletConnector.metadata.groupKey === 'metamask';
193
- const authMode$1 = authMode.getAuthMode();
194
- // in connect only we only allow one account to be connected in the same wallet
195
- // also this is only a feature in MM
196
- // NOTE: WalletConnect doesn't support requestPermissions - it uses enable() to connect
197
- // So we skip chooseAccountsToConnect for WalletConnect connectors
198
- if (authMode$1 !== 'connect-only' &&
199
- isMetamask &&
200
- !walletConnector.isWalletConnect) {
201
- try {
202
- setSelectedWalletConnectorKey({
203
- chain: walletConnector.connectedChain,
204
- key: walletConnector.key,
205
- });
206
- pushView('pending-connect');
207
- yield walletConnector.chooseAccountsToConnect();
208
- }
209
- catch (error) {
210
- if (error instanceof utils.UserRejectedRequestError) {
211
- setErrorMessage('connection_rejected', undefined, 'wallet-account-select');
212
- clearStackAndPushInitialView();
213
- return;
214
- }
215
- }
216
- }
191
+ // Historical MM-only account-picker hook point. Until #11056 the dedicated
192
+ // MetaMaskConnector overrode chooseAccountsToConnect() as a no-op
193
+ // (`return []`), so this block never prompted. #11056 deleted that
194
+ // connector and both replacement paths (metamask-evm SDK connector and the
195
+ // generic injected fallback) inherit the base implementation, which calls
196
+ // wallet_requestPermissions and ALWAYS pops the MetaMask approval UI —
197
+ // blocking flows that re-connect an already-connected MM (external-wallet
198
+ // funding's duplicate-wallet detection, account-switch flows) on a popup
199
+ // that never resolves. Restore the long-standing no-op behavior by not
200
+ // calling it at all.
217
201
  const supportsMultiAccount = walletConnectorCore.connectorRequiresDisconnectionForNewConnection(walletConnector);
218
202
  const isNotInstalledWithQrCodeSupport = walletConnector.isWalletConnect ||
219
203
  (!walletConnector.isInstalledOnBrowser() &&
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
  import { __awaiter } from '../../../../../../_virtual/_tslib.js';
3
- import { StorageService, isMobile, UserRejectedRequestError } from '@dynamic-labs/utils';
3
+ import { StorageService, isMobile } from '@dynamic-labs/utils';
4
4
  import { isPhantomRedirectConnector, connectorRequiresDisconnectionForNewConnection } from '@dynamic-labs/wallet-connector-core';
5
5
  import '../../../../context/DynamicContext/useDynamicContext/useDynamicContext.js';
6
6
  import { useInternalDynamicContext } from '../../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.js';
@@ -9,7 +9,6 @@ import { useMockContext } from '../../../../context/MockContext/MockContext.js';
9
9
  import { useViewContext } from '../../../../context/ViewContext/ViewContext.js';
10
10
  import { dynamicEvents } from '../../../../events/dynamicEvents.js';
11
11
  import { logger } from '../../../../shared/logger.js';
12
- import { getAuthMode } from '../../../../store/state/authMode/authMode.js';
13
12
  import { PHANTOM_SIGNATURE_STATE } from '../../../constants/localStorage.js';
14
13
  import '@dynamic-labs/sdk-api-core';
15
14
  import '../../../constants/values.js';
@@ -37,8 +36,8 @@ import { isSelectedWalletAlreadyConnected } from '../../../functions/isSelectedW
37
36
  const useHandleWalletItem = ({ allowAlreadyConnectedWallet, onQrCodeConnect, onConnectOnly, onCustodialWallet, onInstalledExtension, }) => {
38
37
  const { setMultiWalletWidgetState, setShowAuthFlow, setSelectedWalletConnectorKey, walletConnectorOptions, setQrcodeUri, setDesktopUri, user, linkedWallets, setLegacyIsVerifying, } = useInternalDynamicContext();
39
38
  const { mockedSDK } = useMockContext();
40
- const { setError, setErrorMessage } = useErrorContext();
41
- const { pushView, clearStackAndPushInitialView, replaceView } = useViewContext();
39
+ const { setError } = useErrorContext();
40
+ const { pushView, replaceView } = useViewContext();
42
41
  const handleCustodialWalletClick = (walletConnector) => __awaiter(void 0, void 0, void 0, function* () {
43
42
  logger.debug('handleCustodialWalletClick', walletConnector);
44
43
  const twoStepAuthentication = requiresTwoStepAuthentication(walletConnector);
@@ -185,31 +184,16 @@ const useHandleWalletItem = ({ allowAlreadyConnectedWallet, onQrCodeConnect, onC
185
184
  yield walletConnector.endSession();
186
185
  }
187
186
  setLegacyIsVerifying(false);
188
- const isMetamask = walletConnector.metadata.groupKey === 'metamask';
189
- const authMode = getAuthMode();
190
- // in connect only we only allow one account to be connected in the same wallet
191
- // also this is only a feature in MM
192
- // NOTE: WalletConnect doesn't support requestPermissions - it uses enable() to connect
193
- // So we skip chooseAccountsToConnect for WalletConnect connectors
194
- if (authMode !== 'connect-only' &&
195
- isMetamask &&
196
- !walletConnector.isWalletConnect) {
197
- try {
198
- setSelectedWalletConnectorKey({
199
- chain: walletConnector.connectedChain,
200
- key: walletConnector.key,
201
- });
202
- pushView('pending-connect');
203
- yield walletConnector.chooseAccountsToConnect();
204
- }
205
- catch (error) {
206
- if (error instanceof UserRejectedRequestError) {
207
- setErrorMessage('connection_rejected', undefined, 'wallet-account-select');
208
- clearStackAndPushInitialView();
209
- return;
210
- }
211
- }
212
- }
187
+ // Historical MM-only account-picker hook point. Until #11056 the dedicated
188
+ // MetaMaskConnector overrode chooseAccountsToConnect() as a no-op
189
+ // (`return []`), so this block never prompted. #11056 deleted that
190
+ // connector and both replacement paths (metamask-evm SDK connector and the
191
+ // generic injected fallback) inherit the base implementation, which calls
192
+ // wallet_requestPermissions and ALWAYS pops the MetaMask approval UI —
193
+ // blocking flows that re-connect an already-connected MM (external-wallet
194
+ // funding's duplicate-wallet detection, account-switch flows) on a popup
195
+ // that never resolves. Restore the long-standing no-op behavior by not
196
+ // calling it at all.
213
197
  const supportsMultiAccount = connectorRequiresDisconnectionForNewConnection(walletConnector);
214
198
  const isNotInstalledWithQrCodeSupport = walletConnector.isWalletConnect ||
215
199
  (!walletConnector.isInstalledOnBrowser() &&
@@ -123,6 +123,16 @@ const DynamicBridgeWidget = ({ className, variant = 'modal', iconVariant = 'chai
123
123
  const { showAuthFlow, setShowBridgeWidget, bridgeChains, setShowAuthFlow, connectedWallets, sdkHasLoaded, primaryWallet, setPrimaryWallet, } = useInternalDynamicContext.useInternalDynamicContext();
124
124
  const { widgetRef, inlineControlsRef } = DynamicBridgeWidgetContext.useDynamicBridgeWidgetContext();
125
125
  const { t } = reactI18next.useTranslation();
126
+ // Use refs so the bridgeChains effect only fires when bridgeChains changes,
127
+ // not when the user manually switches the primary wallet.
128
+ // setPrimaryWallet is a useCallback that recreates on every primaryWalletId
129
+ // change, so it must also be a ref to avoid retriggering the effect.
130
+ const primaryWalletRef = React.useRef(primaryWallet);
131
+ primaryWalletRef.current = primaryWallet;
132
+ const connectedWalletsRef = React.useRef(connectedWallets);
133
+ connectedWalletsRef.current = connectedWallets;
134
+ const setPrimaryWalletRef = React.useRef(setPrimaryWallet);
135
+ setPrimaryWalletRef.current = setPrimaryWallet;
126
136
  const closeOnOutsideClick = React.useCallback((e) => {
127
137
  var _a;
128
138
  const target = e.composedPath().shift();
@@ -134,20 +144,22 @@ const DynamicBridgeWidget = ({ className, variant = 'modal', iconVariant = 'chai
134
144
  }, [inlineControlsRef, variant, showAuthFlow, setShowBridgeWidget]);
135
145
  index.useOnClickOutside(widgetRef, closeOnOutsideClick);
136
146
  React.useEffect(() => {
147
+ const currentPrimaryWallet = primaryWalletRef.current;
148
+ const currentConnectedWallets = connectedWalletsRef.current;
137
149
  // no need to update primary wallet if it's already set to the correct chain
138
- if (!primaryWallet ||
150
+ if (!currentPrimaryWallet ||
139
151
  !bridgeChains ||
140
- connectedWallets.length <= 1 ||
141
- primaryWallet.chain === bridgeChains[0].chain) {
152
+ currentConnectedWallets.length <= 1 ||
153
+ currentPrimaryWallet.chain === bridgeChains[0].chain) {
142
154
  return;
143
155
  }
144
156
  // find a connected wallet that matches the primary bridge chain
145
- const newPrimaryWallet = connectedWallets.find((wallet) => wallet.chain === bridgeChains[0].chain);
157
+ const newPrimaryWallet = currentConnectedWallets.find((wallet) => wallet.chain === bridgeChains[0].chain);
146
158
  // if a new primary wallet is found, update the primary wallet
147
159
  if (newPrimaryWallet) {
148
160
  logger.logger.debug('[DynamicBridgeWidget] bridgeChains changed - updating primary wallet', {
149
161
  bridgeChains,
150
- connectedWallets: connectedWallets.map((wallet) => ({
162
+ connectedWallets: currentConnectedWallets.map((wallet) => ({
151
163
  address: wallet.address,
152
164
  chain: wallet.chain,
153
165
  id: wallet.id,
@@ -158,14 +170,14 @@ const DynamicBridgeWidget = ({ className, variant = 'modal', iconVariant = 'chai
158
170
  id: newPrimaryWallet.id,
159
171
  },
160
172
  oldPrimaryWallet: {
161
- address: primaryWallet.address,
162
- chain: primaryWallet.chain,
163
- id: primaryWallet.id,
173
+ address: currentPrimaryWallet.address,
174
+ chain: currentPrimaryWallet.chain,
175
+ id: currentPrimaryWallet.id,
164
176
  },
165
177
  });
166
- setPrimaryWallet(newPrimaryWallet.id);
178
+ setPrimaryWalletRef.current(newPrimaryWallet.id);
167
179
  }
168
- }, [bridgeChains, primaryWallet, connectedWallets, setPrimaryWallet]);
180
+ }, [bridgeChains]);
169
181
  return (jsxRuntime.jsxs(ShadowDOM.ShadowDOM, { id: 'dynamic-bridge-widget', className: 'dynamic-widget__container', children: [jsxRuntime.jsx(InlineWidget.InlineWidget, { ref: inlineControlsRef, className: className, dataTestId: 'BridgeWidgetNav', children: bridgeChains === null || bridgeChains === void 0 ? void 0 : bridgeChains.map(({ chain }) => {
170
182
  const ChainIcon = getChainIcon.getChainIcon(chain);
171
183
  if (!sdkHasLoaded) {
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { useCallback, useEffect } from 'react';
3
+ import { useRef, useCallback, useEffect } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { WalletIcon } from '@dynamic-labs/wallet-book';
6
6
  import '../../components/Accordion/components/AccordionItem/AccordionItem.js';
@@ -119,6 +119,16 @@ const DynamicBridgeWidget = ({ className, variant = 'modal', iconVariant = 'chai
119
119
  const { showAuthFlow, setShowBridgeWidget, bridgeChains, setShowAuthFlow, connectedWallets, sdkHasLoaded, primaryWallet, setPrimaryWallet, } = useInternalDynamicContext();
120
120
  const { widgetRef, inlineControlsRef } = useDynamicBridgeWidgetContext();
121
121
  const { t } = useTranslation();
122
+ // Use refs so the bridgeChains effect only fires when bridgeChains changes,
123
+ // not when the user manually switches the primary wallet.
124
+ // setPrimaryWallet is a useCallback that recreates on every primaryWalletId
125
+ // change, so it must also be a ref to avoid retriggering the effect.
126
+ const primaryWalletRef = useRef(primaryWallet);
127
+ primaryWalletRef.current = primaryWallet;
128
+ const connectedWalletsRef = useRef(connectedWallets);
129
+ connectedWalletsRef.current = connectedWallets;
130
+ const setPrimaryWalletRef = useRef(setPrimaryWallet);
131
+ setPrimaryWalletRef.current = setPrimaryWallet;
122
132
  const closeOnOutsideClick = useCallback((e) => {
123
133
  var _a;
124
134
  const target = e.composedPath().shift();
@@ -130,20 +140,22 @@ const DynamicBridgeWidget = ({ className, variant = 'modal', iconVariant = 'chai
130
140
  }, [inlineControlsRef, variant, showAuthFlow, setShowBridgeWidget]);
131
141
  useOnClickOutside(widgetRef, closeOnOutsideClick);
132
142
  useEffect(() => {
143
+ const currentPrimaryWallet = primaryWalletRef.current;
144
+ const currentConnectedWallets = connectedWalletsRef.current;
133
145
  // no need to update primary wallet if it's already set to the correct chain
134
- if (!primaryWallet ||
146
+ if (!currentPrimaryWallet ||
135
147
  !bridgeChains ||
136
- connectedWallets.length <= 1 ||
137
- primaryWallet.chain === bridgeChains[0].chain) {
148
+ currentConnectedWallets.length <= 1 ||
149
+ currentPrimaryWallet.chain === bridgeChains[0].chain) {
138
150
  return;
139
151
  }
140
152
  // find a connected wallet that matches the primary bridge chain
141
- const newPrimaryWallet = connectedWallets.find((wallet) => wallet.chain === bridgeChains[0].chain);
153
+ const newPrimaryWallet = currentConnectedWallets.find((wallet) => wallet.chain === bridgeChains[0].chain);
142
154
  // if a new primary wallet is found, update the primary wallet
143
155
  if (newPrimaryWallet) {
144
156
  logger.debug('[DynamicBridgeWidget] bridgeChains changed - updating primary wallet', {
145
157
  bridgeChains,
146
- connectedWallets: connectedWallets.map((wallet) => ({
158
+ connectedWallets: currentConnectedWallets.map((wallet) => ({
147
159
  address: wallet.address,
148
160
  chain: wallet.chain,
149
161
  id: wallet.id,
@@ -154,14 +166,14 @@ const DynamicBridgeWidget = ({ className, variant = 'modal', iconVariant = 'chai
154
166
  id: newPrimaryWallet.id,
155
167
  },
156
168
  oldPrimaryWallet: {
157
- address: primaryWallet.address,
158
- chain: primaryWallet.chain,
159
- id: primaryWallet.id,
169
+ address: currentPrimaryWallet.address,
170
+ chain: currentPrimaryWallet.chain,
171
+ id: currentPrimaryWallet.id,
160
172
  },
161
173
  });
162
- setPrimaryWallet(newPrimaryWallet.id);
174
+ setPrimaryWalletRef.current(newPrimaryWallet.id);
163
175
  }
164
- }, [bridgeChains, primaryWallet, connectedWallets, setPrimaryWallet]);
176
+ }, [bridgeChains]);
165
177
  return (jsxs(ShadowDOM, { id: 'dynamic-bridge-widget', className: 'dynamic-widget__container', children: [jsx(InlineWidget, { ref: inlineControlsRef, className: className, dataTestId: 'BridgeWidgetNav', children: bridgeChains === null || bridgeChains === void 0 ? void 0 : bridgeChains.map(({ chain }) => {
166
178
  const ChainIcon = getChainIcon(chain);
167
179
  if (!sdkHasLoaded) {
@@ -0,0 +1,14 @@
1
+ import { FC } from 'react';
2
+ export type CryptoToken = {
3
+ code: string;
4
+ icon?: string;
5
+ name: string;
6
+ };
7
+ type CryptoTokenSelectScreenProps = {
8
+ onClose: () => void;
9
+ onSelectToken: (tokenCode: string) => void;
10
+ tokens: CryptoToken[];
11
+ currentToken: string;
12
+ };
13
+ export declare const CryptoTokenSelectScreen: FC<CryptoTokenSelectScreenProps>;
14
+ export {};
@@ -0,0 +1 @@
1
+ export { CryptoTokenSelectScreen, type CryptoToken, } from './CryptoTokenSelectScreen';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Validates that an externally-sourced image URL is safe to render as an
3
+ * `<img src>`. Token icon URLs come from the MoonPay currencies API response,
4
+ * so they are treated as untrusted input: only absolute HTTPS URLs are allowed,
5
+ * which rules out `javascript:`, `data:`, and other non-network schemes.
6
+ */
7
+ export declare const isTrustedImageUrl: (url: string | undefined) => url is string;
@@ -0,0 +1,30 @@
1
+ import { useInternalDynamicContext } from '../../../../context/DynamicContext/useDynamicContext';
2
+ import type { CryptoToken } from './CryptoTokenSelectScreen';
3
+ export type UseMoonPayOnrampParams = {
4
+ /** Pre-select a token; falls back to the first available currency when invalid. */
5
+ initialToken?: string;
6
+ merchantName?: string;
7
+ /** Override the wallet address; defaults to the primary wallet address. */
8
+ walletAddressOverride?: string;
9
+ /** Called with the signed MoonPay URL instead of opening a popup. */
10
+ onPaymentCreated?: (paymentUrl: string) => void;
11
+ };
12
+ export type UseMoonPayOnrampReturn = {
13
+ primaryWallet: ReturnType<typeof useInternalDynamicContext>['primaryWallet'];
14
+ availableTokens: CryptoToken[];
15
+ tokensLoading: boolean;
16
+ tokensError: boolean;
17
+ selectedToken: string;
18
+ setSelectedToken: (tokenCode: string) => void;
19
+ isOpening: boolean;
20
+ error: string | null;
21
+ effectiveWalletAddress: string;
22
+ openMoonPay: () => void;
23
+ };
24
+ /**
25
+ * Owns the MoonPay onramp data + actions: fetching supported currencies for the
26
+ * connected wallet's chain, tracking the selected token, and opening the signed
27
+ * MoonPay URL (validated against *.moonpay.com over HTTPS). The presentational
28
+ * MoonPayOnramp view consumes this hook.
29
+ */
30
+ export declare const useMoonPayOnramp: ({ initialToken, merchantName, walletAddressOverride, onPaymentCreated, }: UseMoonPayOnrampParams) => UseMoonPayOnrampReturn;