@dynamic-labs/sdk-react-core 4.84.1 → 4.86.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 +30 -0
- package/package.cjs +3 -3
- package/package.js +3 -3
- package/package.json +14 -14
- package/src/index.d.ts +1 -1
- package/src/lib/components/SendBalancePageLayout/components/TokensBalanceDropdown/TokensBalanceDropdown.cjs +5 -1
- package/src/lib/components/SendBalancePageLayout/components/TokensBalanceDropdown/TokensBalanceDropdown.js +5 -1
- package/src/lib/context/OnrampContext/utils/getOnrampProviders.cjs +2 -6
- package/src/lib/context/OnrampContext/utils/getOnrampProviders.js +4 -8
- package/src/lib/data/api/onramp/onramp.cjs +26 -1
- package/src/lib/data/api/onramp/onramp.d.ts +11 -1
- package/src/lib/data/api/onramp/onramp.js +26 -1
- package/src/lib/utils/functions/onrampProviders/index.cjs +1 -0
- package/src/lib/utils/functions/onrampProviders/index.js +1 -0
- package/src/lib/utils/hooks/index.d.ts +1 -1
- package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.cjs +81 -10
- package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.d.ts +18 -2
- package/src/lib/utils/hooks/useAleoShieldedBalances/useAleoShieldedBalances.js +82 -11
- package/src/lib/utils/hooks/useDynamicWaas/useDynamicWaas.cjs +4 -4
- package/src/lib/utils/hooks/useDynamicWaas/useDynamicWaas.js +4 -4
- package/src/lib/utils/hooks/usePrivateTokenBalances/index.d.ts +1 -1
- package/src/lib/utils/hooks/usePrivateTokenBalances/usePrivateTokenBalances.cjs +10 -2
- package/src/lib/utils/hooks/usePrivateTokenBalances/usePrivateTokenBalances.d.ts +17 -2
- package/src/lib/utils/hooks/usePrivateTokenBalances/usePrivateTokenBalances.js +10 -2
- package/src/lib/utils/hooks/useSyncDynamicWaas/instrumentWalletCreation.cjs +60 -0
- package/src/lib/utils/hooks/useSyncDynamicWaas/instrumentWalletCreation.d.ts +29 -0
- package/src/lib/utils/hooks/useSyncDynamicWaas/instrumentWalletCreation.js +55 -0
- package/src/lib/utils/hooks/useSyncDynamicWaas/useSyncDynamicWaas.cjs +14 -24
- package/src/lib/utils/hooks/useSyncDynamicWaas/useSyncDynamicWaas.js +14 -24
- package/src/lib/views/TransactionConfirmationView/TransactionConfirmationView.cjs +17 -1
- package/src/lib/views/TransactionConfirmationView/TransactionConfirmationView.js +17 -1
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/optimisticShield.cjs +28 -12
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/optimisticShield.d.ts +8 -3
- package/src/lib/widgets/DynamicWidget/components/ActiveWalletBalance/optimisticShield.js +28 -12
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import { __awaiter } from '../../../../../_virtual/_tslib.js';
|
|
3
|
-
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
3
|
+
import { useState, useMemo, useRef, useCallback, useEffect } from 'react';
|
|
4
4
|
import { ChainEnum } from '@dynamic-labs/sdk-api-core';
|
|
5
5
|
import '../../../context/DynamicContext/useDynamicContext/useDynamicContext.js';
|
|
6
6
|
import { useInternalDynamicContext } from '../../../context/DynamicContext/useDynamicContext/useInternalDynamicContext/useInternalDynamicContext.js';
|
|
7
|
+
import { useInternalUserWallets } from '../../../context/UserWalletsContext/UserWalletsContext.js';
|
|
7
8
|
import { getAleoCuratedPrices } from '../../../data/api/aleo/getAleoCuratedPrices.js';
|
|
8
9
|
import { getEnvironmentId } from '../../../store/state/dynamicContextProps/dynamicContextProps.js';
|
|
9
10
|
|
|
@@ -279,14 +280,37 @@ const buildTokenBalances = (records, networkId, lookupCurated) => {
|
|
|
279
280
|
* - The connector doesn't expose `listOwnedRecords` (e.g. external Aleo wallet)
|
|
280
281
|
* - The user owns no records that match a known program/record pair
|
|
281
282
|
*/
|
|
282
|
-
const useAleoShieldedBalances = () => {
|
|
283
|
+
const useAleoShieldedBalances = (args = {}) => {
|
|
284
|
+
const { accountAddress, tokenAddresses, includeNativeBalance = true, includeFiat = true, chainName, networkId, } = args;
|
|
283
285
|
const { primaryWallet, network } = useInternalDynamicContext();
|
|
286
|
+
const { userWallets } = useInternalUserWallets();
|
|
284
287
|
const [tokenBalances, setTokenBalances] = useState([]);
|
|
285
288
|
const [isLoading, setIsLoading] = useState(false);
|
|
286
289
|
const [error, setError] = useState();
|
|
287
|
-
|
|
290
|
+
// `accountAddress` is treated as an override of the primary wallet:
|
|
291
|
+
// walk the user-wallets list looking for a matching address (case-
|
|
292
|
+
// insensitive to be tolerant of how the address is formatted at the
|
|
293
|
+
// call site — Aleo addresses are bech32 and casing-insensitive in
|
|
294
|
+
// practice). When no `accountAddress` is provided, fall back to the
|
|
295
|
+
// primary wallet to preserve the existing single-wallet semantics.
|
|
296
|
+
const targetWallet = useMemo(() => {
|
|
297
|
+
var _a;
|
|
298
|
+
if (!accountAddress)
|
|
299
|
+
return primaryWallet;
|
|
300
|
+
const normalized = accountAddress.toLowerCase();
|
|
301
|
+
return ((_a = userWallets.find((w) => { var _a; return ((_a = w.address) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === normalized; })) !== null && _a !== void 0 ? _a : undefined);
|
|
302
|
+
}, [accountAddress, primaryWallet, userWallets]);
|
|
303
|
+
const connector = targetWallet === null || targetWallet === void 0 ? void 0 : targetWallet.connector;
|
|
288
304
|
const isAleo = (connector === null || connector === void 0 ? void 0 : connector.connectedChain) === ChainEnum.Aleo;
|
|
289
|
-
|
|
305
|
+
// When a `chainName` override is passed, treat anything other than
|
|
306
|
+
// `ChainEnum.Aleo` as "the caller explicitly asked for a non-Aleo
|
|
307
|
+
// chain we don't support yet" and short-circuit. Today Aleo is the
|
|
308
|
+
// only chain with a private-balance path, but customers should be
|
|
309
|
+
// able to call the public hook with whatever `chainName` matches
|
|
310
|
+
// their app's active chain and have it transparently no-op.
|
|
311
|
+
const chainNameSupported = chainName === undefined || chainName === ChainEnum.Aleo;
|
|
312
|
+
const supportsShielded = chainNameSupported &&
|
|
313
|
+
isAleo &&
|
|
290
314
|
typeof (connector === null || connector === void 0 ? void 0 : connector.listOwnedRecords) === 'function';
|
|
291
315
|
// Mirror the live connector into a ref so `fetchShielded` can read it
|
|
292
316
|
// without depending on the (often unstable) object reference. Some test
|
|
@@ -296,7 +320,36 @@ const useAleoShieldedBalances = () => {
|
|
|
296
320
|
const connectorRef = useRef(connector);
|
|
297
321
|
connectorRef.current = connector;
|
|
298
322
|
const connectorKey = connector === null || connector === void 0 ? void 0 : connector.key;
|
|
299
|
-
|
|
323
|
+
// When a `networkId` override is provided, use it instead of the
|
|
324
|
+
// context's `network` value when resolving the Aleo network param.
|
|
325
|
+
// Empty string falls back to undefined so `resolveAleoNetwork`
|
|
326
|
+
// returns `aleoNetworkParam: undefined` and the prices fetch is
|
|
327
|
+
// skipped (matching the behaviour when no network is known).
|
|
328
|
+
const effectiveNetwork = networkId !== null && networkId !== void 0 ? networkId : network;
|
|
329
|
+
const networkKey = effectiveNetwork !== undefined && effectiveNetwork !== null
|
|
330
|
+
? String(effectiveNetwork)
|
|
331
|
+
: undefined;
|
|
332
|
+
// Normalise the address filter once per render into a stable
|
|
333
|
+
// primitive key (sorted, comma-joined, lowercased) so the
|
|
334
|
+
// `useCallback` dep below doesn't invalidate on every render when the
|
|
335
|
+
// caller passes a fresh array literal each time (which is the typical
|
|
336
|
+
// pattern in React function components). The matching Set is derived
|
|
337
|
+
// from the same key to keep both memos in lockstep.
|
|
338
|
+
const tokenAddressFilterKey = useMemo(() => {
|
|
339
|
+
if (!tokenAddresses)
|
|
340
|
+
return undefined;
|
|
341
|
+
return [...tokenAddresses]
|
|
342
|
+
.map((a) => a.toLowerCase())
|
|
343
|
+
.sort((a, b) => a.localeCompare(b))
|
|
344
|
+
.join('|');
|
|
345
|
+
}, [tokenAddresses]);
|
|
346
|
+
const tokenAddressFilter = useMemo(() => {
|
|
347
|
+
if (tokenAddressFilterKey === undefined)
|
|
348
|
+
return undefined;
|
|
349
|
+
if (tokenAddressFilterKey === '')
|
|
350
|
+
return new Set();
|
|
351
|
+
return new Set(tokenAddressFilterKey.split('|'));
|
|
352
|
+
}, [tokenAddressFilterKey]);
|
|
300
353
|
const fetchShielded = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
301
354
|
var _a;
|
|
302
355
|
const liveConnector = connectorRef.current;
|
|
@@ -315,9 +368,15 @@ const useAleoShieldedBalances = () => {
|
|
|
315
368
|
// token row, leaving the widget with no price source. Prices are
|
|
316
369
|
// best-effort: a failure leaves balances unpriced rather than
|
|
317
370
|
// dropping the shielded list entirely.
|
|
371
|
+
//
|
|
372
|
+
// When the caller opts out of fiat (`includeFiat: false`) we skip
|
|
373
|
+
// the curated-prices fetch entirely — same semantic as
|
|
374
|
+
// `useTokenBalances` honouring its own `includeFiat` flag. Logos
|
|
375
|
+
// for matched tokens then fall through to the bundled defaults
|
|
376
|
+
// (`ALEO_CREDITS_LOGO`, `UNKNOWN_TOKEN_LOGO`, spec overrides).
|
|
318
377
|
const { safeNetworkId, aleoNetworkParam } = resolveAleoNetwork(networkKey);
|
|
319
378
|
const environmentId = getEnvironmentId();
|
|
320
|
-
const shouldFetchPrices = Boolean(aleoNetworkParam && environmentId);
|
|
379
|
+
const shouldFetchPrices = Boolean(includeFiat && aleoNetworkParam && environmentId);
|
|
321
380
|
const [result, priceList] = yield Promise.all([
|
|
322
381
|
liveConnector.listOwnedRecords(),
|
|
323
382
|
shouldFetchPrices && aleoNetworkParam
|
|
@@ -330,11 +389,16 @@ const useAleoShieldedBalances = () => {
|
|
|
330
389
|
const records = (_a = result === null || result === void 0 ? void 0 : result.records) !== null && _a !== void 0 ? _a : [];
|
|
331
390
|
const lookupCurated = buildCuratedTokenLookup(priceList);
|
|
332
391
|
const balances = [];
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
392
|
+
if (includeNativeBalance) {
|
|
393
|
+
const credits = buildCreditsBalance(records, safeNetworkId, lookupCurated);
|
|
394
|
+
if (credits)
|
|
395
|
+
balances.push(credits);
|
|
396
|
+
}
|
|
336
397
|
balances.push(...buildTokenBalances(records, safeNetworkId, lookupCurated));
|
|
337
|
-
|
|
398
|
+
const filtered = tokenAddressFilter
|
|
399
|
+
? balances.filter((b) => { var _a; return tokenAddressFilter.has(((_a = b.address) !== null && _a !== void 0 ? _a : '').toLowerCase()); })
|
|
400
|
+
: balances;
|
|
401
|
+
setTokenBalances(filtered);
|
|
338
402
|
}
|
|
339
403
|
catch (err) {
|
|
340
404
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -344,7 +408,14 @@ const useAleoShieldedBalances = () => {
|
|
|
344
408
|
finally {
|
|
345
409
|
setIsLoading(false);
|
|
346
410
|
}
|
|
347
|
-
}), [
|
|
411
|
+
}), [
|
|
412
|
+
connectorKey,
|
|
413
|
+
includeFiat,
|
|
414
|
+
includeNativeBalance,
|
|
415
|
+
networkKey,
|
|
416
|
+
supportsShielded,
|
|
417
|
+
tokenAddressFilter,
|
|
418
|
+
]);
|
|
348
419
|
useEffect(() => {
|
|
349
420
|
if (!supportsShielded) {
|
|
350
421
|
setTokenBalances((prev) => (prev.length === 0 ? prev : []));
|
|
@@ -88,7 +88,7 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
|
|
|
88
88
|
};
|
|
89
89
|
const useDynamicWaas = () => {
|
|
90
90
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
91
|
-
const { setShowAuthFlow
|
|
91
|
+
const { setShowAuthFlow } = useInternalDynamicContext.useInternalDynamicContext();
|
|
92
92
|
const { addedWalletsIds, userWallets } = UserWalletsContext.useInternalUserWallets();
|
|
93
93
|
const user = useUser.useUser();
|
|
94
94
|
const apiBaseUrl = dynamicContextProps.useApiBaseUrl();
|
|
@@ -468,8 +468,8 @@ const useDynamicWaas = () => {
|
|
|
468
468
|
});
|
|
469
469
|
const upgradeToDynamicWaas = React.useCallback((_j) => _tslib.__awaiter(void 0, [_j], void 0, function* ({ privateKey, wallet, password, }) {
|
|
470
470
|
isUpgrading.current = true;
|
|
471
|
-
if (!
|
|
472
|
-
throw new utils.DynamicError('
|
|
471
|
+
if (!wallet) {
|
|
472
|
+
throw new utils.DynamicError('Wallet to upgrade was not provided');
|
|
473
473
|
}
|
|
474
474
|
const chainName = wallet.chain;
|
|
475
475
|
const walletId = wallet.id;
|
|
@@ -514,7 +514,7 @@ const useDynamicWaas = () => {
|
|
|
514
514
|
}
|
|
515
515
|
throw new utils.DynamicError('Upgrade failed, please try again.');
|
|
516
516
|
}
|
|
517
|
-
}), [environmentId, getWaasWalletConnector,
|
|
517
|
+
}), [environmentId, getWaasWalletConnector, refresh]);
|
|
518
518
|
/**
|
|
519
519
|
* Returns WaaS wallet instances from `userWallets` filtered by `key === 'dynamicwaas'`.
|
|
520
520
|
*
|
|
@@ -84,7 +84,7 @@ const configWaasWalletConnector = ({ walletConnector, environmentId, apiBaseUrl,
|
|
|
84
84
|
};
|
|
85
85
|
const useDynamicWaas = () => {
|
|
86
86
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
87
|
-
const { setShowAuthFlow
|
|
87
|
+
const { setShowAuthFlow } = useInternalDynamicContext();
|
|
88
88
|
const { addedWalletsIds, userWallets } = useInternalUserWallets();
|
|
89
89
|
const user = useUser();
|
|
90
90
|
const apiBaseUrl = useApiBaseUrl();
|
|
@@ -464,8 +464,8 @@ const useDynamicWaas = () => {
|
|
|
464
464
|
});
|
|
465
465
|
const upgradeToDynamicWaas = useCallback((_j) => __awaiter(void 0, [_j], void 0, function* ({ privateKey, wallet, password, }) {
|
|
466
466
|
isUpgrading.current = true;
|
|
467
|
-
if (!
|
|
468
|
-
throw new DynamicError('
|
|
467
|
+
if (!wallet) {
|
|
468
|
+
throw new DynamicError('Wallet to upgrade was not provided');
|
|
469
469
|
}
|
|
470
470
|
const chainName = wallet.chain;
|
|
471
471
|
const walletId = wallet.id;
|
|
@@ -510,7 +510,7 @@ const useDynamicWaas = () => {
|
|
|
510
510
|
}
|
|
511
511
|
throw new DynamicError('Upgrade failed, please try again.');
|
|
512
512
|
}
|
|
513
|
-
}), [environmentId, getWaasWalletConnector,
|
|
513
|
+
}), [environmentId, getWaasWalletConnector, refresh]);
|
|
514
514
|
/**
|
|
515
515
|
* Returns WaaS wallet instances from `userWallets` filtered by `key === 'dynamicwaas'`.
|
|
516
516
|
*
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { usePrivateTokenBalances } from './usePrivateTokenBalances';
|
|
2
|
-
export type { UsePrivateTokenBalancesReturn } from './usePrivateTokenBalances';
|
|
2
|
+
export type { UsePrivateTokenBalancesArgs, UsePrivateTokenBalancesReturn, } from './usePrivateTokenBalances';
|
|
@@ -5,8 +5,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
|
|
6
6
|
var useAleoShieldedBalances = require('../useAleoShieldedBalances/useAleoShieldedBalances.cjs');
|
|
7
7
|
|
|
8
|
-
const usePrivateTokenBalances = () => {
|
|
9
|
-
const {
|
|
8
|
+
const usePrivateTokenBalances = (args = {}) => {
|
|
9
|
+
const { accountAddress, tokenAddresses, includeNativeBalance = false, includeFiat = false, chainName, networkId, } = args;
|
|
10
|
+
const { tokenBalances, isLoading, error, refetch, supportsShielded } = useAleoShieldedBalances.useAleoShieldedBalances({
|
|
11
|
+
accountAddress,
|
|
12
|
+
chainName,
|
|
13
|
+
includeFiat,
|
|
14
|
+
includeNativeBalance,
|
|
15
|
+
networkId,
|
|
16
|
+
tokenAddresses,
|
|
17
|
+
});
|
|
10
18
|
return {
|
|
11
19
|
error,
|
|
12
20
|
isLoading,
|
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import { TokenBalance } from '@dynamic-labs/sdk-api-core';
|
|
1
|
+
import { ChainEnum, TokenBalance } from '@dynamic-labs/sdk-api-core';
|
|
2
|
+
/**
|
|
3
|
+
* Optional inputs that intentionally mirror the corresponding
|
|
4
|
+
* `useTokenBalances` params so callers can move between the public
|
|
5
|
+
* and private hooks with minimal call-site changes. Boolean defaults
|
|
6
|
+
* match `useTokenBalances` (`includeNativeBalance: false`,
|
|
7
|
+
* `includeFiat: false`) — opt in explicitly when you want either.
|
|
8
|
+
*/
|
|
9
|
+
export type UsePrivateTokenBalancesArgs = {
|
|
10
|
+
accountAddress?: string;
|
|
11
|
+
tokenAddresses?: string[];
|
|
12
|
+
includeNativeBalance?: boolean;
|
|
13
|
+
includeFiat?: boolean;
|
|
14
|
+
chainName?: ChainEnum;
|
|
15
|
+
networkId?: number;
|
|
16
|
+
};
|
|
2
17
|
export type UsePrivateTokenBalancesReturn = {
|
|
3
18
|
tokenBalances: TokenBalance[];
|
|
4
19
|
isLoading: boolean;
|
|
@@ -6,4 +21,4 @@ export type UsePrivateTokenBalancesReturn = {
|
|
|
6
21
|
refetch: () => Promise<void>;
|
|
7
22
|
supportsPrivateBalances: boolean;
|
|
8
23
|
};
|
|
9
|
-
export declare const usePrivateTokenBalances: () => UsePrivateTokenBalancesReturn;
|
|
24
|
+
export declare const usePrivateTokenBalances: (args?: UsePrivateTokenBalancesArgs) => UsePrivateTokenBalancesReturn;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import { useAleoShieldedBalances } from '../useAleoShieldedBalances/useAleoShieldedBalances.js';
|
|
3
3
|
|
|
4
|
-
const usePrivateTokenBalances = () => {
|
|
5
|
-
const {
|
|
4
|
+
const usePrivateTokenBalances = (args = {}) => {
|
|
5
|
+
const { accountAddress, tokenAddresses, includeNativeBalance = false, includeFiat = false, chainName, networkId, } = args;
|
|
6
|
+
const { tokenBalances, isLoading, error, refetch, supportsShielded } = useAleoShieldedBalances({
|
|
7
|
+
accountAddress,
|
|
8
|
+
chainName,
|
|
9
|
+
includeFiat,
|
|
10
|
+
includeNativeBalance,
|
|
11
|
+
networkId,
|
|
12
|
+
tokenAddresses,
|
|
13
|
+
});
|
|
6
14
|
return {
|
|
7
15
|
error,
|
|
8
16
|
isLoading,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
var _tslib = require('../../../../../_virtual/_tslib.cjs');
|
|
7
|
+
require('@dynamic-labs/iconic');
|
|
8
|
+
require('@dynamic-labs/wallet-connector-core');
|
|
9
|
+
require('react');
|
|
10
|
+
require('react/jsx-runtime');
|
|
11
|
+
require('../../../context/ViewContext/ViewContext.cjs');
|
|
12
|
+
var logger = require('../../../shared/logger.cjs');
|
|
13
|
+
require('@dynamic-labs/wallet-book');
|
|
14
|
+
require('@dynamic-labs/utils');
|
|
15
|
+
require('../../constants/colors.cjs');
|
|
16
|
+
require('../../constants/values.cjs');
|
|
17
|
+
require('@dynamic-labs/sdk-api-core');
|
|
18
|
+
require('../../../shared/consts/index.cjs');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Soft watchdog for the embedded-wallet auto-creation ceremony. Long enough to
|
|
22
|
+
* clear a healthy MPC ceremony (observed ~7s, iframe load timeout is 10s) so a
|
|
23
|
+
* fire here means the flow genuinely stalled rather than just being slow.
|
|
24
|
+
*/
|
|
25
|
+
const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
|
|
26
|
+
/**
|
|
27
|
+
* Wraps the auto-wallet-creation ceremony so that "silent" failures carry a
|
|
28
|
+
* reason. Without this, a ceremony that hangs (promise never settles) or a user
|
|
29
|
+
* who navigates away mid-flow produces an `initiated` log with no `success` or
|
|
30
|
+
* `failed` counterpart, leaving the failure undiagnosable.
|
|
31
|
+
*
|
|
32
|
+
* Emits:
|
|
33
|
+
* - `auto_wallet_creation_timeout` if `run` has not settled within `timeoutMs`.
|
|
34
|
+
* - `auto_wallet_creation_abandoned` if the page is hidden while `run` is still
|
|
35
|
+
* in flight (relies on the logger's keepalive transport to survive unload).
|
|
36
|
+
*
|
|
37
|
+
* The watchdog is observational: it never aborts `run`, and its return value /
|
|
38
|
+
* rejection is passed through unchanged.
|
|
39
|
+
*/
|
|
40
|
+
const instrumentWalletCreation = (_a) => _tslib.__awaiter(void 0, [_a], void 0, function* ({ run, logData, timeoutMs = AUTO_WALLET_CREATION_TIMEOUT_MS, }) {
|
|
41
|
+
var _b, _c;
|
|
42
|
+
const startTime = Date.now();
|
|
43
|
+
const timeoutId = setTimeout(() => {
|
|
44
|
+
logger.logger.instrument('Auto wallet creation timed out', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_timeout', reason: 'timeout', time: Date.now() - startTime }));
|
|
45
|
+
}, timeoutMs);
|
|
46
|
+
const handlePageHide = () => {
|
|
47
|
+
logger.logger.instrument('Auto wallet creation abandoned', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_abandoned', reason: 'page_hidden', time: Date.now() - startTime }));
|
|
48
|
+
};
|
|
49
|
+
(_b = globalThis.addEventListener) === null || _b === void 0 ? void 0 : _b.call(globalThis, 'pagehide', handlePageHide);
|
|
50
|
+
try {
|
|
51
|
+
return yield run();
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
clearTimeout(timeoutId);
|
|
55
|
+
(_c = globalThis.removeEventListener) === null || _c === void 0 ? void 0 : _c.call(globalThis, 'pagehide', handlePageHide);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
exports.AUTO_WALLET_CREATION_TIMEOUT_MS = AUTO_WALLET_CREATION_TIMEOUT_MS;
|
|
60
|
+
exports.instrumentWalletCreation = instrumentWalletCreation;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soft watchdog for the embedded-wallet auto-creation ceremony. Long enough to
|
|
3
|
+
* clear a healthy MPC ceremony (observed ~7s, iframe load timeout is 10s) so a
|
|
4
|
+
* fire here means the flow genuinely stalled rather than just being slow.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
|
|
7
|
+
type InstrumentWalletCreationArgs<T> = {
|
|
8
|
+
/** The wallet-creation work to run and observe (the MPC ceremony). */
|
|
9
|
+
run: () => Promise<T>;
|
|
10
|
+
/** Shared fields attached to every emitted event (chains, env, user). */
|
|
11
|
+
logData: Record<string, unknown>;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Wraps the auto-wallet-creation ceremony so that "silent" failures carry a
|
|
16
|
+
* reason. Without this, a ceremony that hangs (promise never settles) or a user
|
|
17
|
+
* who navigates away mid-flow produces an `initiated` log with no `success` or
|
|
18
|
+
* `failed` counterpart, leaving the failure undiagnosable.
|
|
19
|
+
*
|
|
20
|
+
* Emits:
|
|
21
|
+
* - `auto_wallet_creation_timeout` if `run` has not settled within `timeoutMs`.
|
|
22
|
+
* - `auto_wallet_creation_abandoned` if the page is hidden while `run` is still
|
|
23
|
+
* in flight (relies on the logger's keepalive transport to survive unload).
|
|
24
|
+
*
|
|
25
|
+
* The watchdog is observational: it never aborts `run`, and its return value /
|
|
26
|
+
* rejection is passed through unchanged.
|
|
27
|
+
*/
|
|
28
|
+
export declare const instrumentWalletCreation: <T>({ run, logData, timeoutMs, }: InstrumentWalletCreationArgs<T>) => Promise<T>;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { __awaiter } from '../../../../../_virtual/_tslib.js';
|
|
3
|
+
import '@dynamic-labs/iconic';
|
|
4
|
+
import '@dynamic-labs/wallet-connector-core';
|
|
5
|
+
import 'react';
|
|
6
|
+
import 'react/jsx-runtime';
|
|
7
|
+
import '../../../context/ViewContext/ViewContext.js';
|
|
8
|
+
import { logger } from '../../../shared/logger.js';
|
|
9
|
+
import '@dynamic-labs/wallet-book';
|
|
10
|
+
import '@dynamic-labs/utils';
|
|
11
|
+
import '../../constants/colors.js';
|
|
12
|
+
import '../../constants/values.js';
|
|
13
|
+
import '@dynamic-labs/sdk-api-core';
|
|
14
|
+
import '../../../shared/consts/index.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Soft watchdog for the embedded-wallet auto-creation ceremony. Long enough to
|
|
18
|
+
* clear a healthy MPC ceremony (observed ~7s, iframe load timeout is 10s) so a
|
|
19
|
+
* fire here means the flow genuinely stalled rather than just being slow.
|
|
20
|
+
*/
|
|
21
|
+
const AUTO_WALLET_CREATION_TIMEOUT_MS = 30000;
|
|
22
|
+
/**
|
|
23
|
+
* Wraps the auto-wallet-creation ceremony so that "silent" failures carry a
|
|
24
|
+
* reason. Without this, a ceremony that hangs (promise never settles) or a user
|
|
25
|
+
* who navigates away mid-flow produces an `initiated` log with no `success` or
|
|
26
|
+
* `failed` counterpart, leaving the failure undiagnosable.
|
|
27
|
+
*
|
|
28
|
+
* Emits:
|
|
29
|
+
* - `auto_wallet_creation_timeout` if `run` has not settled within `timeoutMs`.
|
|
30
|
+
* - `auto_wallet_creation_abandoned` if the page is hidden while `run` is still
|
|
31
|
+
* in flight (relies on the logger's keepalive transport to survive unload).
|
|
32
|
+
*
|
|
33
|
+
* The watchdog is observational: it never aborts `run`, and its return value /
|
|
34
|
+
* rejection is passed through unchanged.
|
|
35
|
+
*/
|
|
36
|
+
const instrumentWalletCreation = (_a) => __awaiter(void 0, [_a], void 0, function* ({ run, logData, timeoutMs = AUTO_WALLET_CREATION_TIMEOUT_MS, }) {
|
|
37
|
+
var _b, _c;
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
const timeoutId = setTimeout(() => {
|
|
40
|
+
logger.instrument('Auto wallet creation timed out', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_timeout', reason: 'timeout', time: Date.now() - startTime }));
|
|
41
|
+
}, timeoutMs);
|
|
42
|
+
const handlePageHide = () => {
|
|
43
|
+
logger.instrument('Auto wallet creation abandoned', Object.assign(Object.assign({}, logData), { key: 'auto_wallet_creation_abandoned', reason: 'page_hidden', time: Date.now() - startTime }));
|
|
44
|
+
};
|
|
45
|
+
(_b = globalThis.addEventListener) === null || _b === void 0 ? void 0 : _b.call(globalThis, 'pagehide', handlePageHide);
|
|
46
|
+
try {
|
|
47
|
+
return yield run();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
clearTimeout(timeoutId);
|
|
51
|
+
(_c = globalThis.removeEventListener) === null || _c === void 0 ? void 0 : _c.call(globalThis, 'pagehide', handlePageHide);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export { AUTO_WALLET_CREATION_TIMEOUT_MS, instrumentWalletCreation };
|
|
@@ -41,6 +41,7 @@ var useMutation = require('../useMutation/useMutation.cjs');
|
|
|
41
41
|
var useSetupPassword = require('../useSetupPassword/useSetupPassword.cjs');
|
|
42
42
|
var useWalletDelegation = require('../useWalletDelegation/useWalletDelegation.cjs');
|
|
43
43
|
require('../useWalletDelegation/DelegationError.cjs');
|
|
44
|
+
var instrumentWalletCreation = require('./instrumentWalletCreation.cjs');
|
|
44
45
|
|
|
45
46
|
// Validate if all required conditions are met for wallet creation
|
|
46
47
|
const useWalletCreationValidation = () => {
|
|
@@ -77,31 +78,28 @@ const useWalletCreation = () => {
|
|
|
77
78
|
return useMutation.useMutation((requirements) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
78
79
|
const startTime = Date.now();
|
|
79
80
|
const chainsString = requirements.map((req) => req.chain).join(',');
|
|
80
|
-
|
|
81
|
-
logger.logger.instrument('Auto wallet creation initiated', {
|
|
81
|
+
const baseLogData = {
|
|
82
82
|
chainCount: requirements.length,
|
|
83
83
|
chains: chainsString,
|
|
84
84
|
environmentId,
|
|
85
|
-
key: 'auto_wallet_creation_initiated',
|
|
86
|
-
time: 0,
|
|
87
85
|
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
88
|
-
}
|
|
86
|
+
};
|
|
87
|
+
// Log wallet creation initiation to DataDog
|
|
88
|
+
logger.logger.instrument('Auto wallet creation initiated', Object.assign(Object.assign({}, baseLogData), { key: 'auto_wallet_creation_initiated', time: 0 }));
|
|
89
89
|
try {
|
|
90
90
|
// If passcodeRequired is enabled, prompt user to set up password first
|
|
91
91
|
const password = yield setupPassword();
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
// Wrap the ceremony so a hang or mid-flow page exit emits a reason
|
|
93
|
+
// instead of leaving a silent "initiated" with no resolution.
|
|
94
|
+
yield instrumentWalletCreation.instrumentWalletCreation({
|
|
95
|
+
logData: baseLogData,
|
|
96
|
+
run: () => createWalletAccount(requirements, password, undefined, {
|
|
97
|
+
skipCloseAuthFlow: Boolean(password),
|
|
98
|
+
}),
|
|
94
99
|
});
|
|
95
100
|
const duration = Date.now() - startTime;
|
|
96
101
|
// Log successful wallet creation to DataDog
|
|
97
|
-
logger.logger.instrument('Auto wallet creation successful', {
|
|
98
|
-
chainCount: requirements.length,
|
|
99
|
-
chains: chainsString,
|
|
100
|
-
environmentId,
|
|
101
|
-
key: 'auto_wallet_creation_success',
|
|
102
|
-
time: duration,
|
|
103
|
-
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
104
|
-
});
|
|
102
|
+
logger.logger.instrument('Auto wallet creation successful', Object.assign(Object.assign({}, baseLogData), { key: 'auto_wallet_creation_success', time: duration }));
|
|
105
103
|
}
|
|
106
104
|
catch (error) {
|
|
107
105
|
const duration = Date.now() - startTime;
|
|
@@ -110,15 +108,7 @@ const useWalletCreation = () => {
|
|
|
110
108
|
// UI for this case; the metric should be consistent with that intent.
|
|
111
109
|
if (!(error instanceof Error &&
|
|
112
110
|
error.message === useSetupPassword.PASSWORD_SETUP_CANCELLED_ERROR)) {
|
|
113
|
-
logger.logger.instrument('Auto wallet creation failed', {
|
|
114
|
-
chainCount: requirements.length,
|
|
115
|
-
chains: chainsString,
|
|
116
|
-
environmentId,
|
|
117
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
118
|
-
key: 'auto_wallet_creation_failed',
|
|
119
|
-
time: duration,
|
|
120
|
-
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
121
|
-
});
|
|
111
|
+
logger.logger.instrument('Auto wallet creation failed', Object.assign(Object.assign({}, baseLogData), { error: error instanceof Error ? error.message : 'Unknown error', key: 'auto_wallet_creation_failed', time: duration }));
|
|
122
112
|
}
|
|
123
113
|
throw error;
|
|
124
114
|
}
|
|
@@ -37,6 +37,7 @@ import { useMutation } from '../useMutation/useMutation.js';
|
|
|
37
37
|
import { useSetupPassword, PASSWORD_SETUP_CANCELLED_ERROR } from '../useSetupPassword/useSetupPassword.js';
|
|
38
38
|
import { useWalletDelegation } from '../useWalletDelegation/useWalletDelegation.js';
|
|
39
39
|
import '../useWalletDelegation/DelegationError.js';
|
|
40
|
+
import { instrumentWalletCreation } from './instrumentWalletCreation.js';
|
|
40
41
|
|
|
41
42
|
// Validate if all required conditions are met for wallet creation
|
|
42
43
|
const useWalletCreationValidation = () => {
|
|
@@ -73,31 +74,28 @@ const useWalletCreation = () => {
|
|
|
73
74
|
return useMutation((requirements) => __awaiter(void 0, void 0, void 0, function* () {
|
|
74
75
|
const startTime = Date.now();
|
|
75
76
|
const chainsString = requirements.map((req) => req.chain).join(',');
|
|
76
|
-
|
|
77
|
-
logger.instrument('Auto wallet creation initiated', {
|
|
77
|
+
const baseLogData = {
|
|
78
78
|
chainCount: requirements.length,
|
|
79
79
|
chains: chainsString,
|
|
80
80
|
environmentId,
|
|
81
|
-
key: 'auto_wallet_creation_initiated',
|
|
82
|
-
time: 0,
|
|
83
81
|
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
84
|
-
}
|
|
82
|
+
};
|
|
83
|
+
// Log wallet creation initiation to DataDog
|
|
84
|
+
logger.instrument('Auto wallet creation initiated', Object.assign(Object.assign({}, baseLogData), { key: 'auto_wallet_creation_initiated', time: 0 }));
|
|
85
85
|
try {
|
|
86
86
|
// If passcodeRequired is enabled, prompt user to set up password first
|
|
87
87
|
const password = yield setupPassword();
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// Wrap the ceremony so a hang or mid-flow page exit emits a reason
|
|
89
|
+
// instead of leaving a silent "initiated" with no resolution.
|
|
90
|
+
yield instrumentWalletCreation({
|
|
91
|
+
logData: baseLogData,
|
|
92
|
+
run: () => createWalletAccount(requirements, password, undefined, {
|
|
93
|
+
skipCloseAuthFlow: Boolean(password),
|
|
94
|
+
}),
|
|
90
95
|
});
|
|
91
96
|
const duration = Date.now() - startTime;
|
|
92
97
|
// Log successful wallet creation to DataDog
|
|
93
|
-
logger.instrument('Auto wallet creation successful', {
|
|
94
|
-
chainCount: requirements.length,
|
|
95
|
-
chains: chainsString,
|
|
96
|
-
environmentId,
|
|
97
|
-
key: 'auto_wallet_creation_success',
|
|
98
|
-
time: duration,
|
|
99
|
-
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
100
|
-
});
|
|
98
|
+
logger.instrument('Auto wallet creation successful', Object.assign(Object.assign({}, baseLogData), { key: 'auto_wallet_creation_success', time: duration }));
|
|
101
99
|
}
|
|
102
100
|
catch (error) {
|
|
103
101
|
const duration = Date.now() - startTime;
|
|
@@ -106,15 +104,7 @@ const useWalletCreation = () => {
|
|
|
106
104
|
// UI for this case; the metric should be consistent with that intent.
|
|
107
105
|
if (!(error instanceof Error &&
|
|
108
106
|
error.message === PASSWORD_SETUP_CANCELLED_ERROR)) {
|
|
109
|
-
logger.instrument('Auto wallet creation failed', {
|
|
110
|
-
chainCount: requirements.length,
|
|
111
|
-
chains: chainsString,
|
|
112
|
-
environmentId,
|
|
113
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
114
|
-
key: 'auto_wallet_creation_failed',
|
|
115
|
-
time: duration,
|
|
116
|
-
userId: user === null || user === void 0 ? void 0 : user.id,
|
|
117
|
-
});
|
|
107
|
+
logger.instrument('Auto wallet creation failed', Object.assign(Object.assign({}, baseLogData), { error: error instanceof Error ? error.message : 'Unknown error', key: 'auto_wallet_creation_failed', time: duration }));
|
|
118
108
|
}
|
|
119
109
|
throw error;
|
|
120
110
|
}
|
|
@@ -312,7 +312,23 @@ const TransactionConfirmationView = ({ transaction, onError, onSuccess, mutation
|
|
|
312
312
|
if (transaction.fetchFee) {
|
|
313
313
|
const feePromise = transaction.fetchFee();
|
|
314
314
|
if (feePromise && typeof feePromise.finally === 'function') {
|
|
315
|
-
feePromise.finally(
|
|
315
|
+
feePromise.finally(() => {
|
|
316
|
+
// `transaction.isGasSponsored()` is a synchronous getter that
|
|
317
|
+
// reads a flag the transaction caches during `fetchFee`. On
|
|
318
|
+
// Aleo we can't know whether Feemaster covers the call until
|
|
319
|
+
// that async lookup resolves, so the flag starts at `false`
|
|
320
|
+
// and flips to `true` once the policy answer lands. The
|
|
321
|
+
// initial-mount `useEffect` below only runs when
|
|
322
|
+
// `transaction` itself changes, so without re-reading here
|
|
323
|
+
// React's `isGasSponsored` state stays `false` and the
|
|
324
|
+
// confirmation card renders the gas row with a `--` value
|
|
325
|
+
// (since `fee.gas = 0n` for sponsored calls). Re-read once
|
|
326
|
+
// the cached flag is populated so the row hides itself.
|
|
327
|
+
if (transaction.isGasSponsored) {
|
|
328
|
+
setIsGasSponsored(transaction.isGasSponsored());
|
|
329
|
+
}
|
|
330
|
+
update();
|
|
331
|
+
});
|
|
316
332
|
}
|
|
317
333
|
}
|
|
318
334
|
}, [primaryWallet, transaction, update]);
|
|
@@ -304,7 +304,23 @@ const TransactionConfirmationView = ({ transaction, onError, onSuccess, mutation
|
|
|
304
304
|
if (transaction.fetchFee) {
|
|
305
305
|
const feePromise = transaction.fetchFee();
|
|
306
306
|
if (feePromise && typeof feePromise.finally === 'function') {
|
|
307
|
-
feePromise.finally(
|
|
307
|
+
feePromise.finally(() => {
|
|
308
|
+
// `transaction.isGasSponsored()` is a synchronous getter that
|
|
309
|
+
// reads a flag the transaction caches during `fetchFee`. On
|
|
310
|
+
// Aleo we can't know whether Feemaster covers the call until
|
|
311
|
+
// that async lookup resolves, so the flag starts at `false`
|
|
312
|
+
// and flips to `true` once the policy answer lands. The
|
|
313
|
+
// initial-mount `useEffect` below only runs when
|
|
314
|
+
// `transaction` itself changes, so without re-reading here
|
|
315
|
+
// React's `isGasSponsored` state stays `false` and the
|
|
316
|
+
// confirmation card renders the gas row with a `--` value
|
|
317
|
+
// (since `fee.gas = 0n` for sponsored calls). Re-read once
|
|
318
|
+
// the cached flag is populated so the row hides itself.
|
|
319
|
+
if (transaction.isGasSponsored) {
|
|
320
|
+
setIsGasSponsored(transaction.isGasSponsored());
|
|
321
|
+
}
|
|
322
|
+
update();
|
|
323
|
+
});
|
|
308
324
|
}
|
|
309
325
|
}
|
|
310
326
|
}, [primaryWallet, transaction, update]);
|