@hsuite/native-connect-angular 1.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/coverage/coverage-summary.json +53 -49
- package/coverage/index.html +100 -100
- package/coverage/lcov-report/index.html +100 -100
- package/coverage/lcov-report/lib/components/account-selector/account-actions/account-actions.component.ts.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-actions/index.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-filter/account-filter.component.ts.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-filter/index.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-formatting.service.ts.html +13 -19
- package/coverage/lcov-report/lib/components/account-selector/account-grouping.service.ts.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-list/account-list.component.ts.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-list/index.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-selector.component.ts.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/account-selector.service.ts.html +1 -1
- package/coverage/lcov-report/lib/components/account-selector/index.html +5 -5
- package/coverage/lcov-report/lib/components/wallet-account-display/index.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-account-display/wallet-account-display.component.ts.html +10 -10
- package/coverage/lcov-report/lib/components/wallet-connect-button/index.html +21 -21
- package/coverage/lcov-report/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +475 -451
- package/coverage/lcov-report/lib/components/wallet-connect-prompt/index.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-connected-guard/index.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +148 -148
- package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/index.html +17 -17
- package/coverage/lcov-report/lib/components/wallet-connection-modal/index.html +21 -21
- package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/index.html +17 -17
- package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +838 -838
- package/coverage/lcov-report/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +1230 -987
- package/coverage/lcov-report/lib/components/wallet-session-display/index.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-session-display/wallet-session-display.component.ts.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-transaction-status/index.html +1 -1
- package/coverage/lcov-report/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +1 -1
- package/coverage/lcov-report/lib/directives/index.html +1 -1
- package/coverage/lcov-report/lib/directives/wallet-connected.directive.ts.html +1 -1
- package/coverage/lcov-report/lib/directives/wallet-context.directive.ts.html +1 -1
- package/coverage/lcov-report/lib/directives/wallet-events.directive.ts.html +1 -1
- package/coverage/lcov-report/lib/hsuite-wallet.module.ts.html +1 -1
- package/coverage/lcov-report/lib/index.html +1 -1
- package/coverage/lcov-report/lib/models/connection-config.model.ts.html +1 -1
- package/coverage/lcov-report/lib/models/index.html +7 -7
- package/coverage/lcov-report/lib/models/provider-types.ts.html +11 -5
- package/coverage/lcov-report/lib/providers/base-wallet-provider.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/hsuite-native/channel-client.service.ts.html +9 -6
- package/coverage/lcov-report/lib/providers/hsuite-native/index.html +10 -10
- package/coverage/lcov-report/lib/providers/hsuite-native-provider.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/index.html +1 -1
- package/coverage/lcov-report/lib/providers/p2p-native/index.html +7 -7
- package/coverage/lcov-report/lib/providers/p2p-native/p2p-native.provider.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/p2p-native/p2p-session-manager.ts.html +7 -4
- package/coverage/lcov-report/lib/providers/wallet-error-handler.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/walletconnect/core/index.html +10 -10
- package/coverage/lcov-report/lib/providers/walletconnect/core/session-health.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-provider.ts.html +10 -7
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/walletconnect/signers/hedera-signer.ts.html +1 -1
- package/coverage/lcov-report/lib/providers/walletconnect/signers/index.html +1 -1
- package/coverage/lcov-report/lib/providers/walletconnect/signers/signer-factory.ts.html +49 -49
- package/coverage/lcov-report/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1 -1
- package/coverage/lcov-report/lib/services/hsuite-auth.interceptor.ts.html +568 -0
- package/coverage/lcov-report/lib/services/index.html +50 -20
- package/coverage/lcov-report/lib/services/logger.service.ts.html +4 -4
- package/coverage/lcov-report/lib/services/smart-session.service.ts.html +1264 -0
- package/coverage/lcov-report/lib/services/transaction-builders/active-account-source.ts.html +250 -0
- package/coverage/lcov-report/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1 -1
- package/coverage/lcov-report/lib/services/transaction-builders/hedera-amount-utils.ts.html +1 -1
- package/coverage/lcov-report/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +318 -318
- package/coverage/lcov-report/lib/services/transaction-builders/index.html +24 -9
- package/coverage/lcov-report/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +1 -1
- package/coverage/lcov-report/lib/services/transaction.service.ts.html +7 -10
- package/coverage/lcov-report/lib/services/unified-wallet.service.ts.html +9 -12
- package/coverage/lcov-report/lib/services/wallet-context.service.ts.html +1 -1
- package/coverage/lcov-report/lib/services/wallet-event-bus.service.ts.html +1 -1
- package/coverage/lcov-report/lib/services/wallet-providers.service.ts.html +4 -7
- package/coverage/lcov-report/lib/transports/chrome-extension-transport.ts.html +1 -1
- package/coverage/lcov-report/lib/transports/index.html +1 -1
- package/coverage/lcov-report/lib/utils/index.html +36 -21
- package/coverage/lcov-report/lib/utils/ledger-icons.util.ts.html +254 -161
- package/coverage/lcov-report/lib/utils/ledger-ui-registry.ts.html +676 -0
- package/coverage/lcov.info +3236 -2122
- package/coverage/lib/components/account-selector/account-actions/account-actions.component.ts.html +1 -1
- package/coverage/lib/components/account-selector/account-actions/index.html +1 -1
- package/coverage/lib/components/account-selector/account-filter/account-filter.component.ts.html +1 -1
- package/coverage/lib/components/account-selector/account-filter/index.html +1 -1
- package/coverage/lib/components/account-selector/account-formatting.service.ts.html +13 -19
- package/coverage/lib/components/account-selector/account-grouping.service.ts.html +1 -1
- package/coverage/lib/components/account-selector/account-list/account-list.component.ts.html +1 -1
- package/coverage/lib/components/account-selector/account-list/index.html +1 -1
- package/coverage/lib/components/account-selector/account-selector.component.ts.html +1 -1
- package/coverage/lib/components/account-selector/account-selector.service.ts.html +1 -1
- package/coverage/lib/components/account-selector/index.html +5 -5
- package/coverage/lib/components/wallet-account-display/index.html +1 -1
- package/coverage/lib/components/wallet-account-display/wallet-account-display.component.ts.html +10 -10
- package/coverage/lib/components/wallet-connect-button/index.html +21 -21
- package/coverage/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +475 -451
- package/coverage/lib/components/wallet-connect-prompt/index.html +1 -1
- package/coverage/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +1 -1
- package/coverage/lib/components/wallet-connected-guard/index.html +1 -1
- package/coverage/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +1 -1
- package/coverage/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +148 -148
- package/coverage/lib/components/wallet-connection-modal/connection-method-step/index.html +17 -17
- package/coverage/lib/components/wallet-connection-modal/index.html +21 -21
- package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/index.html +17 -17
- package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +838 -838
- package/coverage/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +1230 -987
- package/coverage/lib/components/wallet-session-display/index.html +1 -1
- package/coverage/lib/components/wallet-session-display/wallet-session-display.component.ts.html +1 -1
- package/coverage/lib/components/wallet-transaction-status/index.html +1 -1
- package/coverage/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +1 -1
- package/coverage/lib/directives/index.html +1 -1
- package/coverage/lib/directives/wallet-connected.directive.ts.html +1 -1
- package/coverage/lib/directives/wallet-context.directive.ts.html +1 -1
- package/coverage/lib/directives/wallet-events.directive.ts.html +1 -1
- package/coverage/lib/hsuite-wallet.module.ts.html +1 -1
- package/coverage/lib/index.html +1 -1
- package/coverage/lib/models/connection-config.model.ts.html +1 -1
- package/coverage/lib/models/index.html +7 -7
- package/coverage/lib/models/provider-types.ts.html +11 -5
- package/coverage/lib/providers/base-wallet-provider.ts.html +1 -1
- package/coverage/lib/providers/hsuite-native/channel-client.service.ts.html +9 -6
- package/coverage/lib/providers/hsuite-native/index.html +10 -10
- package/coverage/lib/providers/hsuite-native-provider.ts.html +1 -1
- package/coverage/lib/providers/index.html +1 -1
- package/coverage/lib/providers/p2p-native/index.html +7 -7
- package/coverage/lib/providers/p2p-native/p2p-native.provider.ts.html +1 -1
- package/coverage/lib/providers/p2p-native/p2p-session-manager.ts.html +7 -4
- package/coverage/lib/providers/wallet-error-handler.ts.html +1 -1
- package/coverage/lib/providers/walletconnect/core/index.html +10 -10
- package/coverage/lib/providers/walletconnect/core/session-health.ts.html +1 -1
- package/coverage/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1 -1
- package/coverage/lib/providers/walletconnect/core/walletconnect-provider.ts.html +10 -7
- package/coverage/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +1 -1
- package/coverage/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +1 -1
- package/coverage/lib/providers/walletconnect/signers/hedera-signer.ts.html +1 -1
- package/coverage/lib/providers/walletconnect/signers/index.html +1 -1
- package/coverage/lib/providers/walletconnect/signers/signer-factory.ts.html +49 -49
- package/coverage/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1 -1
- package/coverage/lib/services/hsuite-auth.interceptor.ts.html +568 -0
- package/coverage/lib/services/index.html +50 -20
- package/coverage/lib/services/logger.service.ts.html +4 -4
- package/coverage/lib/services/smart-session.service.ts.html +1264 -0
- package/coverage/lib/services/transaction-builders/active-account-source.ts.html +250 -0
- package/coverage/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1 -1
- package/coverage/lib/services/transaction-builders/hedera-amount-utils.ts.html +1 -1
- package/coverage/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +318 -318
- package/coverage/lib/services/transaction-builders/index.html +24 -9
- package/coverage/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +1 -1
- package/coverage/lib/services/transaction.service.ts.html +7 -10
- package/coverage/lib/services/unified-wallet.service.ts.html +9 -12
- package/coverage/lib/services/wallet-context.service.ts.html +1 -1
- package/coverage/lib/services/wallet-event-bus.service.ts.html +1 -1
- package/coverage/lib/services/wallet-providers.service.ts.html +4 -7
- package/coverage/lib/transports/chrome-extension-transport.ts.html +1 -1
- package/coverage/lib/transports/index.html +1 -1
- package/coverage/lib/utils/index.html +36 -21
- package/coverage/lib/utils/ledger-icons.util.ts.html +254 -161
- package/coverage/lib/utils/ledger-ui-registry.ts.html +676 -0
- package/dist/fesm2022/hsuite-native-connect-angular.mjs +939 -328
- package/dist/fesm2022/hsuite-native-connect-angular.mjs.map +1 -1
- package/dist/index.d.ts +489 -33
- package/package.json +4 -4
- package/src/index.ts +26 -0
- package/src/lib/components/account-selector/account-formatting.service.ts +8 -10
- package/src/lib/components/wallet-account-display/wallet-account-display.component.ts +9 -9
- package/src/lib/components/wallet-connect-button/wallet-connect-button.component.spec.ts +89 -0
- package/src/lib/components/wallet-connect-button/wallet-connect-button.component.ts +8 -0
- package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.html +1 -6
- package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.spec.ts +173 -0
- package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts +118 -37
- package/src/lib/models/provider-types.ts +3 -1
- package/src/lib/models/unified-account.model.ts +4 -1
- package/src/lib/providers/hsuite-native/channel-client.service.ts +1 -0
- package/src/lib/providers/p2p-native/p2p-session-manager.ts +1 -0
- package/src/lib/providers/walletconnect/core/walletconnect-provider.ts +2 -1
- package/src/lib/services/hsuite-auth.interceptor.ts +159 -0
- package/src/lib/services/smart-session.service.ts +378 -0
- package/src/lib/services/transaction-builders/active-account-source.spec.ts +75 -0
- package/src/lib/services/transaction-builders/active-account-source.ts +55 -0
- package/src/lib/services/transaction-builders/hedera-transaction-builder.service.ts +4 -4
- package/src/lib/services/transaction-builders/index.ts +1 -0
- package/src/lib/services/transaction.service.ts +7 -4
- package/src/lib/services/unified-wallet.service.spec.ts +1 -1
- package/src/lib/services/unified-wallet.service.ts +2 -3
- package/src/lib/services/wallet-providers.service.ts +0 -1
- package/src/lib/utils/index.ts +1 -0
- package/src/lib/utils/ledger-icons.util.ts +61 -30
- package/src/lib/utils/ledger-ui-registry.ts +197 -0
|
@@ -14,7 +14,8 @@ import SignClient from '@walletconnect/sign-client';
|
|
|
14
14
|
import { toObservable } from '@angular/core/rxjs-interop';
|
|
15
15
|
import { copyToClipboard } from '@hsuite/native-connect-ui';
|
|
16
16
|
import * as QRCode from 'qrcode';
|
|
17
|
-
import { Subscription } from 'rxjs';
|
|
17
|
+
import { Subscription, firstValueFrom, catchError, from, switchMap, throwError } from 'rxjs';
|
|
18
|
+
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
18
19
|
import { PublicKey, KeyList, TransferTransaction, AccountId, Hbar, TokenId, NftId, TokenAssociateTransaction, TokenDissociateTransaction, ScheduleCreateTransaction, TokenCreateTransaction, TokenType, TokenSupplyType, TokenMintTransaction, TokenBurnTransaction, TopicCreateTransaction, TopicMessageSubmitTransaction, AccountCreateTransaction, PrivateKey, AccountUpdateTransaction, TransactionId, BatchTransaction, Client } from '@hashgraph/sdk';
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -197,6 +198,120 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
197
198
|
type: Output
|
|
198
199
|
}] } });
|
|
199
200
|
|
|
201
|
+
/**
|
|
202
|
+
* HSuite Native Connect
|
|
203
|
+
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
204
|
+
*
|
|
205
|
+
* SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
|
206
|
+
*
|
|
207
|
+
* This file is part of HSuite Native Connect. For commercial licensing,
|
|
208
|
+
* visit https://hsuite.finance/licensing
|
|
209
|
+
*/
|
|
210
|
+
/**
|
|
211
|
+
* Open, string-keyed registry of {@link LedgerUiDescriptor}s.
|
|
212
|
+
*
|
|
213
|
+
* A static registry (like `AccountLabelManager`) because ledger presentation is
|
|
214
|
+
* process-global — Ionicons' own icon table is global too. Mirrors the core
|
|
215
|
+
* `LedgerRegistry` shape (`register` / `get` / `has` / `list`) so the two read
|
|
216
|
+
* the same way.
|
|
217
|
+
*/
|
|
218
|
+
class LedgerUIRegistry {
|
|
219
|
+
static descriptors = new Map();
|
|
220
|
+
/** Whether {@link registerIcons} has already flushed SVGs to Ionicons. */
|
|
221
|
+
static iconsFlushed = false;
|
|
222
|
+
/**
|
|
223
|
+
* Register (or replace) the presentation descriptor for a ledger.
|
|
224
|
+
*
|
|
225
|
+
* If icons were already flushed to Ionicons (i.e. {@link registerIcons} ran),
|
|
226
|
+
* the new ledger's SVG is registered immediately so late registrations still
|
|
227
|
+
* render.
|
|
228
|
+
*
|
|
229
|
+
* @param descriptor - The ledger's presentation contract.
|
|
230
|
+
*/
|
|
231
|
+
static register(descriptor) {
|
|
232
|
+
LedgerUIRegistry.descriptors.set(descriptor.id, descriptor);
|
|
233
|
+
if (LedgerUIRegistry.iconsFlushed) {
|
|
234
|
+
addIcons({ [descriptor.iconName]: descriptor.iconSvg });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Look up a ledger's presentation descriptor.
|
|
239
|
+
*
|
|
240
|
+
* @param ledgerId - Canonical ledger id (case-sensitive; callers normalise).
|
|
241
|
+
* @returns The descriptor, or `undefined` if the ledger is not registered.
|
|
242
|
+
*/
|
|
243
|
+
static get(ledgerId) {
|
|
244
|
+
return LedgerUIRegistry.descriptors.get(ledgerId);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* @param ledgerId - Canonical ledger id.
|
|
248
|
+
* @returns `true` if a descriptor is registered for `ledgerId`.
|
|
249
|
+
*/
|
|
250
|
+
static has(ledgerId) {
|
|
251
|
+
return LedgerUIRegistry.descriptors.has(ledgerId);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* @returns All registered descriptors, in registration order.
|
|
255
|
+
*/
|
|
256
|
+
static list() {
|
|
257
|
+
return Array.from(LedgerUIRegistry.descriptors.values());
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Flush every registered ledger's brand SVG to Ionicons so the icons resolve
|
|
261
|
+
* by name from `<ion-icon>`. Idempotent and cheap to call repeatedly — call it
|
|
262
|
+
* before the icons are first rendered (e.g. from a component constructor or
|
|
263
|
+
* once at app bootstrap).
|
|
264
|
+
*/
|
|
265
|
+
static registerIcons() {
|
|
266
|
+
if (LedgerUIRegistry.iconsFlushed) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const icons = {};
|
|
270
|
+
for (const descriptor of LedgerUIRegistry.descriptors.values()) {
|
|
271
|
+
icons[descriptor.iconName] = descriptor.iconSvg;
|
|
272
|
+
}
|
|
273
|
+
addIcons(icons);
|
|
274
|
+
LedgerUIRegistry.iconsFlushed = true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
// Built-in ledgers
|
|
279
|
+
//
|
|
280
|
+
// The only place the first-party ledger set is enumerated. Adding a new
|
|
281
|
+
// first-party chain is one more `register(...)` here; everything downstream
|
|
282
|
+
// (icons, colours, names, the connection modal's "Choose Blockchain" step)
|
|
283
|
+
// derives from these descriptors.
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
LedgerUIRegistry.register({
|
|
286
|
+
id: 'hedera',
|
|
287
|
+
displayName: 'Hedera',
|
|
288
|
+
shortName: 'Hedera',
|
|
289
|
+
currencySymbol: 'HBAR',
|
|
290
|
+
currencyDecimals: 8,
|
|
291
|
+
brandColor: '#8259ef',
|
|
292
|
+
badgeColor: 'success',
|
|
293
|
+
iconName: 'ledger-hedera',
|
|
294
|
+
// Brand disc themed with `brandColor` (#8259ef) + a white "H" so the mark stays
|
|
295
|
+
// brand-correct and legible on any background (incl. the dark modal card).
|
|
296
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none"><path fill="#8259ef" d="M12 4a8 8 0 1 0 0 16a8 8 0 0 0 0-16"/><path fill="#fff" d="M15.252 15.46h-1.016V13.3H9.764v2.16H8.748V8.456h1.016v2.108h4.472V8.456h1.016zm-5.44-2.968h4.472v-1.116H9.812z"/></g></svg>`,
|
|
297
|
+
tokenIconUrl: 'https://assets.coingecko.com/coins/images/3688/standard/hbar.png?1696504364',
|
|
298
|
+
});
|
|
299
|
+
LedgerUIRegistry.register({
|
|
300
|
+
id: 'xrpl',
|
|
301
|
+
displayName: 'XRP Ledger',
|
|
302
|
+
shortName: 'XRPL',
|
|
303
|
+
currencySymbol: 'XRP',
|
|
304
|
+
currencyDecimals: 6,
|
|
305
|
+
brandColor: '#23292f',
|
|
306
|
+
badgeColor: 'tertiary',
|
|
307
|
+
iconName: 'ledger-xrpl',
|
|
308
|
+
// `currentColor` so the mark adopts the host `<ion-icon>` colour — the XRPL
|
|
309
|
+
// brand black (#23292f) is invisible on the dark modal card, so the icon
|
|
310
|
+
// inherits the theme colour while `brandColor` stays the canonical accent.
|
|
311
|
+
iconSvg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M21.778 4h-2.837l-4.49 4.371a3.52 3.52 0 0 1-2.451.99a3.52 3.52 0 0 1-2.452-.99L5.062 4h-2.84L8.13 9.754c2.14 2.083 5.607 2.083 7.745 0zM2.223 20H5.05l4.508-4.385a3.5 3.5 0 0 1 2.443-.985c.914 0 1.792.354 2.443.985L18.952 20h2.826l-5.92-5.761c-2.132-2.073-5.585-2.073-7.715 0z"/></svg>`,
|
|
312
|
+
tokenIconUrl: 'https://assets.coingecko.com/coins/images/44/standard/xrp-symbol-white-128.png?1696501442',
|
|
313
|
+
});
|
|
314
|
+
|
|
200
315
|
/**
|
|
201
316
|
* HSuite Native Connect
|
|
202
317
|
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
@@ -241,11 +356,9 @@ class AccountFormattingService {
|
|
|
241
356
|
* @returns Display name
|
|
242
357
|
*/
|
|
243
358
|
getLedgerName(ledgerId) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
};
|
|
248
|
-
return names[ledgerId?.toLowerCase()] || ledgerId?.toUpperCase() || 'Unknown';
|
|
359
|
+
return (LedgerUIRegistry.get(ledgerId?.toLowerCase())?.shortName ||
|
|
360
|
+
ledgerId?.toUpperCase() ||
|
|
361
|
+
'Unknown');
|
|
249
362
|
}
|
|
250
363
|
/**
|
|
251
364
|
* Get color for a ledger badge.
|
|
@@ -254,11 +367,7 @@ class AccountFormattingService {
|
|
|
254
367
|
* @returns Ionic color name
|
|
255
368
|
*/
|
|
256
369
|
getLedgerColor(ledgerId) {
|
|
257
|
-
|
|
258
|
-
hedera: 'success',
|
|
259
|
-
xrpl: 'tertiary',
|
|
260
|
-
};
|
|
261
|
-
return colors[ledgerId?.toLowerCase()] || 'medium';
|
|
370
|
+
return LedgerUIRegistry.get(ledgerId?.toLowerCase())?.badgeColor || 'medium';
|
|
262
371
|
}
|
|
263
372
|
/**
|
|
264
373
|
* Format network display name with proper casing.
|
|
@@ -651,61 +760,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
651
760
|
*/
|
|
652
761
|
/**
|
|
653
762
|
* @module LedgerIconsUtil
|
|
654
|
-
* Utilities for getting default icon URLs for ledgers and their native tokens.
|
|
655
763
|
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
764
|
+
* Backward-compatible facade over {@link LedgerUIRegistry}.
|
|
765
|
+
*
|
|
766
|
+
* Every value here is **derived from the registry** so there is exactly one
|
|
767
|
+
* source of truth for ledger presentation. New code should prefer
|
|
768
|
+
* {@link LedgerUIRegistry} directly (it is open/extensible and richer); these
|
|
769
|
+
* getters and constant maps remain for the existing public API surface.
|
|
770
|
+
*
|
|
771
|
+
* The constant maps below are snapshots taken at module load — they include the
|
|
772
|
+
* built-in ledgers (Hedera, XRPL). Ledgers registered later at runtime are
|
|
773
|
+
* always reachable through the getters and {@link LedgerUIRegistry}, but will
|
|
774
|
+
* not retroactively appear in these legacy snapshots; prefer the registry when
|
|
775
|
+
* extensibility matters.
|
|
658
776
|
*/
|
|
659
777
|
/**
|
|
660
|
-
* Default icon URLs for native ledger tokens
|
|
778
|
+
* Default raster icon URLs for native ledger tokens, keyed by ledger id.
|
|
779
|
+
* Derived from {@link LedgerUiDescriptor.tokenIconUrl}.
|
|
661
780
|
*/
|
|
662
|
-
const DEFAULT_LEDGER_ICONS =
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
};
|
|
781
|
+
const DEFAULT_LEDGER_ICONS = Object.fromEntries(LedgerUIRegistry.list()
|
|
782
|
+
.filter((descriptor) => descriptor.tokenIconUrl)
|
|
783
|
+
.map((descriptor) => [descriptor.id, descriptor.tokenIconUrl]));
|
|
666
784
|
/**
|
|
667
|
-
* Brand
|
|
785
|
+
* Brand colours (hex) for each ledger, keyed by ledger id.
|
|
786
|
+
* Derived from {@link LedgerUiDescriptor.brandColor}.
|
|
668
787
|
*/
|
|
669
|
-
const LEDGER_COLORS =
|
|
670
|
-
hedera: '#8259ef',
|
|
671
|
-
xrpl: '#23292f',
|
|
672
|
-
};
|
|
788
|
+
const LEDGER_COLORS = Object.fromEntries(LedgerUIRegistry.list().map((descriptor) => [descriptor.id, descriptor.brandColor]));
|
|
673
789
|
/**
|
|
674
|
-
* Display names for each ledger
|
|
790
|
+
* Display names for each ledger, keyed by ledger id.
|
|
791
|
+
* Derived from {@link LedgerUiDescriptor.displayName}.
|
|
675
792
|
*/
|
|
676
|
-
const LEDGER_NAMES =
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
793
|
+
const LEDGER_NAMES = Object.fromEntries(LedgerUIRegistry.list().map((descriptor) => [descriptor.id, descriptor.displayName]));
|
|
794
|
+
/**
|
|
795
|
+
* Ionicons names under which each ledger's brand SVG is registered, keyed by
|
|
796
|
+
* ledger id. Reference these from `<ion-icon [name]="...">`.
|
|
797
|
+
* Derived from {@link LedgerUiDescriptor.iconName}.
|
|
798
|
+
*/
|
|
799
|
+
const LEDGER_ICON_NAMES = Object.fromEntries(LedgerUIRegistry.list().map((descriptor) => [descriptor.id, descriptor.iconName]));
|
|
680
800
|
/**
|
|
681
|
-
*
|
|
682
|
-
* @
|
|
683
|
-
*
|
|
801
|
+
* Register the ledger brand SVGs with Ionicons so they can be referenced by name
|
|
802
|
+
* (see {@link LEDGER_ICON_NAMES}). Idempotent; safe to call from multiple
|
|
803
|
+
* consumers. Delegates to {@link LedgerUIRegistry.registerIcons}.
|
|
804
|
+
*/
|
|
805
|
+
function registerLedgerIcons() {
|
|
806
|
+
LedgerUIRegistry.registerIcons();
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Get the default raster token icon URL for a ledger.
|
|
810
|
+
* @param ledgerId - Ledger identifier (e.g. `'hedera'`, `'xrpl'`)
|
|
811
|
+
* @returns Icon URL, or empty string if the ledger has none.
|
|
684
812
|
*/
|
|
685
813
|
function getLedgerIcon(ledgerId) {
|
|
686
|
-
return
|
|
814
|
+
return LedgerUIRegistry.get(ledgerId?.toLowerCase())?.tokenIconUrl ?? '';
|
|
687
815
|
}
|
|
688
816
|
/**
|
|
689
|
-
* Get the brand
|
|
817
|
+
* Get the brand colour (hex) for a ledger.
|
|
690
818
|
* @param ledgerId - Ledger identifier
|
|
691
|
-
* @returns Hex
|
|
819
|
+
* @returns Hex colour string, or the Ionic primary CSS variable as fallback.
|
|
692
820
|
*/
|
|
693
821
|
function getLedgerColor(ledgerId) {
|
|
694
|
-
return
|
|
822
|
+
return LedgerUIRegistry.get(ledgerId?.toLowerCase())?.brandColor ?? 'var(--ion-color-primary)';
|
|
695
823
|
}
|
|
696
824
|
/**
|
|
697
|
-
* Get the display name for a ledger
|
|
825
|
+
* Get the display name for a ledger.
|
|
698
826
|
* @param ledgerId - Ledger identifier
|
|
699
|
-
* @returns Human-readable ledger name
|
|
827
|
+
* @returns Human-readable ledger name, or the raw id if unknown.
|
|
700
828
|
*/
|
|
701
829
|
function getLedgerName(ledgerId) {
|
|
702
|
-
return
|
|
830
|
+
return LedgerUIRegistry.get(ledgerId?.toLowerCase())?.displayName ?? ledgerId;
|
|
703
831
|
}
|
|
704
832
|
/**
|
|
705
|
-
* Get token icon URL, falling back to ledger default if not provided
|
|
833
|
+
* Get a token icon URL, falling back to the ledger default if not provided.
|
|
706
834
|
* @param tokenIconUrl - Specific token icon URL (optional)
|
|
707
835
|
* @param ledgerId - Ledger identifier for fallback
|
|
708
|
-
* @returns Icon URL or empty string
|
|
836
|
+
* @returns Icon URL, or empty string.
|
|
709
837
|
*/
|
|
710
838
|
function getTokenIcon(tokenIconUrl, ledgerId) {
|
|
711
839
|
return tokenIconUrl || getLedgerIcon(ledgerId);
|
|
@@ -1301,7 +1429,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
1301
1429
|
* Each event is available as both an Angular EventEmitter (for template bindings)
|
|
1302
1430
|
* and an RxJS Observable (for advanced composition with operators).
|
|
1303
1431
|
*/
|
|
1304
|
-
const logger$
|
|
1432
|
+
const logger$k = getLogger().scoped?.('WalletEventBus') ?? getLogger();
|
|
1305
1433
|
/**
|
|
1306
1434
|
* Centralized event bus for wallet events.
|
|
1307
1435
|
*
|
|
@@ -1413,7 +1541,7 @@ class WalletEventBus {
|
|
|
1413
1541
|
*
|
|
1414
1542
|
*/
|
|
1415
1543
|
constructor() {
|
|
1416
|
-
logger$
|
|
1544
|
+
logger$k.debug('Event bus initialized');
|
|
1417
1545
|
}
|
|
1418
1546
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletEventBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1419
1547
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletEventBus, providedIn: 'root' });
|
|
@@ -1600,7 +1728,7 @@ class BaseWalletProvider {
|
|
|
1600
1728
|
* }
|
|
1601
1729
|
* ```
|
|
1602
1730
|
*/
|
|
1603
|
-
const logger$
|
|
1731
|
+
const logger$j = getLogger().scoped?.('ChannelClientService') ?? getLogger();
|
|
1604
1732
|
/**
|
|
1605
1733
|
* ChannelClientService
|
|
1606
1734
|
*
|
|
@@ -1650,6 +1778,7 @@ class ChannelClientService {
|
|
|
1650
1778
|
return {
|
|
1651
1779
|
id: `channel-${account.address}`,
|
|
1652
1780
|
address: account.address,
|
|
1781
|
+
publicKey: account.publicKey,
|
|
1653
1782
|
label: account.alias ?? `Account ${index + 1}`,
|
|
1654
1783
|
ledgerId: account.ledgerId,
|
|
1655
1784
|
networkId: account.networkId,
|
|
@@ -1688,7 +1817,7 @@ class ChannelClientService {
|
|
|
1688
1817
|
*/
|
|
1689
1818
|
async connect(config) {
|
|
1690
1819
|
if (this._state() === 'connecting' || this._state() === 'pending') {
|
|
1691
|
-
logger$
|
|
1820
|
+
logger$j.warn('Connection already in progress');
|
|
1692
1821
|
const existing = this._currentInvite();
|
|
1693
1822
|
if (existing)
|
|
1694
1823
|
return existing;
|
|
@@ -1730,7 +1859,7 @@ class ChannelClientService {
|
|
|
1730
1859
|
this.runInZone(() => {
|
|
1731
1860
|
const previousState = this._state();
|
|
1732
1861
|
if (state !== previousState) {
|
|
1733
|
-
logger$
|
|
1862
|
+
logger$j.debug('State change via callback', { from: previousState, to: state });
|
|
1734
1863
|
this._state.set(state);
|
|
1735
1864
|
// Handle error state - this is triggered on rejection
|
|
1736
1865
|
if (state === 'error') {
|
|
@@ -1755,7 +1884,7 @@ class ChannelClientService {
|
|
|
1755
1884
|
},
|
|
1756
1885
|
onAccountsChange: (accounts) => {
|
|
1757
1886
|
this.runInZone(() => {
|
|
1758
|
-
logger$
|
|
1887
|
+
logger$j.debug('Accounts change via callback', { count: accounts.length });
|
|
1759
1888
|
this._accounts.set(accounts);
|
|
1760
1889
|
// Re-persist when accounts change
|
|
1761
1890
|
if (this._state() === 'active') {
|
|
@@ -1766,7 +1895,7 @@ class ChannelClientService {
|
|
|
1766
1895
|
onTransportChange: (transport) => {
|
|
1767
1896
|
this.runInZone(() => {
|
|
1768
1897
|
if (transport !== this._transportState()) {
|
|
1769
|
-
logger$
|
|
1898
|
+
logger$j.debug('Transport change via callback', { state: transport });
|
|
1770
1899
|
this._transportState.set(transport);
|
|
1771
1900
|
}
|
|
1772
1901
|
});
|
|
@@ -1801,7 +1930,7 @@ class ChannelClientService {
|
|
|
1801
1930
|
});
|
|
1802
1931
|
// Store invite for reconnection
|
|
1803
1932
|
this.storeInvite(invite);
|
|
1804
|
-
logger$
|
|
1933
|
+
logger$j.info('Channel connection initiated', {
|
|
1805
1934
|
channelId: invite.id.slice(0, 8),
|
|
1806
1935
|
type: invite.type,
|
|
1807
1936
|
});
|
|
@@ -1809,7 +1938,7 @@ class ChannelClientService {
|
|
|
1809
1938
|
}
|
|
1810
1939
|
catch (error) {
|
|
1811
1940
|
const message = error instanceof Error ? error.message : String(error);
|
|
1812
|
-
logger$
|
|
1941
|
+
logger$j.error('Connection failed', { error: message });
|
|
1813
1942
|
// Reset connecting flag on error
|
|
1814
1943
|
this.connectingNewSession = false;
|
|
1815
1944
|
this.runInZone(() => {
|
|
@@ -1850,7 +1979,7 @@ class ChannelClientService {
|
|
|
1850
1979
|
if (state !== 'active' && state !== 'approved') {
|
|
1851
1980
|
throw new Error(`Not connected - channel state is '${state}'. Please reconnect to the wallet.`);
|
|
1852
1981
|
}
|
|
1853
|
-
logger$
|
|
1982
|
+
logger$j.debug('Sending RPC request', { method, state });
|
|
1854
1983
|
return this.client.request(method, params, timeoutMs);
|
|
1855
1984
|
}
|
|
1856
1985
|
/**
|
|
@@ -1927,7 +2056,7 @@ class ChannelClientService {
|
|
|
1927
2056
|
await this.client.disconnect();
|
|
1928
2057
|
}
|
|
1929
2058
|
catch (error) {
|
|
1930
|
-
logger$
|
|
2059
|
+
logger$j.warn('Disconnect error', { error });
|
|
1931
2060
|
}
|
|
1932
2061
|
this.client = null;
|
|
1933
2062
|
}
|
|
@@ -1939,7 +2068,7 @@ class ChannelClientService {
|
|
|
1939
2068
|
this._currentInvite.set(null);
|
|
1940
2069
|
this._error.set(null);
|
|
1941
2070
|
});
|
|
1942
|
-
logger$
|
|
2071
|
+
logger$j.info('Disconnected from channel');
|
|
1943
2072
|
}
|
|
1944
2073
|
/**
|
|
1945
2074
|
* Attempt to restore a previous session.
|
|
@@ -1952,7 +2081,7 @@ class ChannelClientService {
|
|
|
1952
2081
|
async attemptRestore() {
|
|
1953
2082
|
// Skip restore if a new connection is being initiated
|
|
1954
2083
|
if (this.connectingNewSession) {
|
|
1955
|
-
logger$
|
|
2084
|
+
logger$j.debug('Skipping restore - new connection in progress');
|
|
1956
2085
|
return false;
|
|
1957
2086
|
}
|
|
1958
2087
|
const stored = this.getStoredChannel();
|
|
@@ -1968,11 +2097,11 @@ class ChannelClientService {
|
|
|
1968
2097
|
// Safe to do unconditionally at this point in the project's lifecycle:
|
|
1969
2098
|
// no dApp sessions are live in production yet.
|
|
1970
2099
|
if (stored.localContext === undefined) {
|
|
1971
|
-
logger$
|
|
2100
|
+
logger$j.warn('Clearing legacy persisted channel without localContext — user must re-approve the dApp connection (§21.1)', { channelId: stored.id.slice(0, 8) });
|
|
1972
2101
|
this.clearStoredChannel();
|
|
1973
2102
|
return false;
|
|
1974
2103
|
}
|
|
1975
|
-
logger$
|
|
2104
|
+
logger$j.info('Attempting session restore', {
|
|
1976
2105
|
channelId: stored.id.slice(0, 8),
|
|
1977
2106
|
});
|
|
1978
2107
|
this.runInZone(() => {
|
|
@@ -1985,7 +2114,7 @@ class ChannelClientService {
|
|
|
1985
2114
|
this.runInZone(() => {
|
|
1986
2115
|
const previousState = this._state();
|
|
1987
2116
|
if (state !== previousState) {
|
|
1988
|
-
logger$
|
|
2117
|
+
logger$j.debug('State change via callback (reconnect)', {
|
|
1989
2118
|
from: previousState,
|
|
1990
2119
|
to: state,
|
|
1991
2120
|
});
|
|
@@ -2005,7 +2134,7 @@ class ChannelClientService {
|
|
|
2005
2134
|
},
|
|
2006
2135
|
onAccountsChange: (accounts) => {
|
|
2007
2136
|
this.runInZone(() => {
|
|
2008
|
-
logger$
|
|
2137
|
+
logger$j.debug('Accounts change via callback (reconnect)', { count: accounts.length });
|
|
2009
2138
|
this._accounts.set(accounts);
|
|
2010
2139
|
if (this._state() === 'active') {
|
|
2011
2140
|
this.persistCurrentState();
|
|
@@ -2015,7 +2144,7 @@ class ChannelClientService {
|
|
|
2015
2144
|
onTransportChange: (transport) => {
|
|
2016
2145
|
this.runInZone(() => {
|
|
2017
2146
|
if (transport !== this._transportState()) {
|
|
2018
|
-
logger$
|
|
2147
|
+
logger$j.debug('Transport change via callback (reconnect)', { state: transport });
|
|
2019
2148
|
this._transportState.set(transport);
|
|
2020
2149
|
}
|
|
2021
2150
|
});
|
|
@@ -2038,14 +2167,14 @@ class ChannelClientService {
|
|
|
2038
2167
|
}
|
|
2039
2168
|
this._state.set('active');
|
|
2040
2169
|
});
|
|
2041
|
-
logger$
|
|
2170
|
+
logger$j.info('Session restored successfully', {
|
|
2042
2171
|
channelId: stored.id.slice(0, 8),
|
|
2043
2172
|
accounts: restoredAccounts.length,
|
|
2044
2173
|
});
|
|
2045
2174
|
return true;
|
|
2046
2175
|
}
|
|
2047
2176
|
catch (error) {
|
|
2048
|
-
logger$
|
|
2177
|
+
logger$j.warn('Session restore failed', {
|
|
2049
2178
|
error: error instanceof Error ? error.message : String(error),
|
|
2050
2179
|
});
|
|
2051
2180
|
// Clean up the partially initialized client
|
|
@@ -2121,7 +2250,7 @@ class ChannelClientService {
|
|
|
2121
2250
|
}
|
|
2122
2251
|
if (clientTransport !== this._transportState()) {
|
|
2123
2252
|
this._transportState.set(clientTransport);
|
|
2124
|
-
logger$
|
|
2253
|
+
logger$j.debug('Transport state updated', { state: clientTransport });
|
|
2125
2254
|
}
|
|
2126
2255
|
});
|
|
2127
2256
|
// Continue polling as long as client exists
|
|
@@ -2164,14 +2293,14 @@ class ChannelClientService {
|
|
|
2164
2293
|
const persisted = this.client.exportState();
|
|
2165
2294
|
if (persisted) {
|
|
2166
2295
|
localStorage.setItem(ChannelClientService.STORAGE_KEY, JSON.stringify(persisted));
|
|
2167
|
-
logger$
|
|
2296
|
+
logger$j.debug('Channel state persisted', {
|
|
2168
2297
|
channelId: persisted.id.slice(0, 8),
|
|
2169
2298
|
accounts: persisted.accounts?.length ?? 0,
|
|
2170
2299
|
});
|
|
2171
2300
|
}
|
|
2172
2301
|
}
|
|
2173
2302
|
catch (error) {
|
|
2174
|
-
logger$
|
|
2303
|
+
logger$j.warn('Failed to persist channel state', { error });
|
|
2175
2304
|
}
|
|
2176
2305
|
}
|
|
2177
2306
|
/**
|
|
@@ -2212,7 +2341,7 @@ class ChannelClientService {
|
|
|
2212
2341
|
}
|
|
2213
2342
|
}
|
|
2214
2343
|
catch {
|
|
2215
|
-
logger$
|
|
2344
|
+
logger$j.warn('Failed to store channel');
|
|
2216
2345
|
}
|
|
2217
2346
|
}
|
|
2218
2347
|
/**
|
|
@@ -2226,7 +2355,7 @@ class ChannelClientService {
|
|
|
2226
2355
|
}
|
|
2227
2356
|
}
|
|
2228
2357
|
catch {
|
|
2229
|
-
logger$
|
|
2358
|
+
logger$j.warn('Failed to retrieve stored channel');
|
|
2230
2359
|
}
|
|
2231
2360
|
return null;
|
|
2232
2361
|
}
|
|
@@ -2278,7 +2407,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
2278
2407
|
* 8. P2P upgrade happens automatically in background
|
|
2279
2408
|
* ```
|
|
2280
2409
|
*/
|
|
2281
|
-
const logger$
|
|
2410
|
+
const logger$i = getLogger().scoped?.('HsuiteNativeProvider') ?? getLogger();
|
|
2282
2411
|
/**
|
|
2283
2412
|
* HSuite Native Connect Provider.
|
|
2284
2413
|
*
|
|
@@ -2364,7 +2493,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2364
2493
|
const isConnected = this.channelService.isConnected();
|
|
2365
2494
|
// If we were connected but now disconnected, emit disconnect event
|
|
2366
2495
|
if (this.previouslyConnected && !isConnected && status === 'disconnected') {
|
|
2367
|
-
logger$
|
|
2496
|
+
logger$i.info('[HsuiteNativeProvider] Channel terminated');
|
|
2368
2497
|
this.eventBus.disconnected.emit({
|
|
2369
2498
|
providerId: this.id,
|
|
2370
2499
|
providerType: 'hsuite-native',
|
|
@@ -2377,7 +2506,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2377
2506
|
});
|
|
2378
2507
|
// Attempt automatic reconnect when session exists after reload
|
|
2379
2508
|
void this.attemptReconnect().catch((error) => {
|
|
2380
|
-
logger$
|
|
2509
|
+
logger$i.debug('[HsuiteNativeProvider] Auto-reconnect skipped', { error });
|
|
2381
2510
|
});
|
|
2382
2511
|
}
|
|
2383
2512
|
/**
|
|
@@ -2471,10 +2600,10 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2471
2600
|
// Guard: Don't start a new connection if already connecting
|
|
2472
2601
|
const currentStatus = this.status();
|
|
2473
2602
|
if (currentStatus === 'connecting') {
|
|
2474
|
-
logger$
|
|
2603
|
+
logger$i.warn('Connection already in progress, ignoring duplicate request');
|
|
2475
2604
|
return;
|
|
2476
2605
|
}
|
|
2477
|
-
logger$
|
|
2606
|
+
logger$i.info('Connecting to HSuite Wallet via unified channel protocol', { config });
|
|
2478
2607
|
try {
|
|
2479
2608
|
const invite = await this.channelService.connect({
|
|
2480
2609
|
type: 'session',
|
|
@@ -2500,14 +2629,14 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2500
2629
|
accounts,
|
|
2501
2630
|
timestamp: Date.now(),
|
|
2502
2631
|
});
|
|
2503
|
-
logger$
|
|
2632
|
+
logger$i.info('Connected successfully via unified channel', {
|
|
2504
2633
|
channelId: invite.id.slice(0, 8),
|
|
2505
2634
|
accountCount: accounts.length,
|
|
2506
2635
|
});
|
|
2507
2636
|
});
|
|
2508
2637
|
}
|
|
2509
2638
|
catch (error) {
|
|
2510
|
-
logger$
|
|
2639
|
+
logger$i.error('Connection failed', { error });
|
|
2511
2640
|
throw error;
|
|
2512
2641
|
}
|
|
2513
2642
|
}
|
|
@@ -2540,7 +2669,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2540
2669
|
if (wallet) {
|
|
2541
2670
|
// Send invite directly via BroadcastChannel - instant, no network!
|
|
2542
2671
|
this.localDiscoveryService.sendInvite(wallet.fingerprint, inviteUrl, config?.appId, config?.appName);
|
|
2543
|
-
logger$
|
|
2672
|
+
logger$i.info('[HsuiteNativeProvider] Sent invite via local discovery', {
|
|
2544
2673
|
wallet: wallet.fingerprint.slice(0, 8),
|
|
2545
2674
|
});
|
|
2546
2675
|
return;
|
|
@@ -2632,7 +2761,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2632
2761
|
* @throws {Error} If disconnection fails (event still emitted)
|
|
2633
2762
|
*/
|
|
2634
2763
|
async disconnect() {
|
|
2635
|
-
logger$
|
|
2764
|
+
logger$i.info('Disconnecting from HSuite Wallet');
|
|
2636
2765
|
try {
|
|
2637
2766
|
await this.channelService.disconnect();
|
|
2638
2767
|
// Emit disconnection event
|
|
@@ -2642,10 +2771,10 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2642
2771
|
reason: 'user_initiated',
|
|
2643
2772
|
timestamp: Date.now(),
|
|
2644
2773
|
});
|
|
2645
|
-
logger$
|
|
2774
|
+
logger$i.info('Disconnected successfully');
|
|
2646
2775
|
}
|
|
2647
2776
|
catch (error) {
|
|
2648
|
-
logger$
|
|
2777
|
+
logger$i.error('Disconnection failed', { error });
|
|
2649
2778
|
// Still emit event even if disconnect fails
|
|
2650
2779
|
this.eventBus.disconnected.emit({
|
|
2651
2780
|
providerId: this.id,
|
|
@@ -2673,7 +2802,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2673
2802
|
* @throws {Error} If channel communication fails
|
|
2674
2803
|
*/
|
|
2675
2804
|
async signTransaction(options) {
|
|
2676
|
-
logger$
|
|
2805
|
+
logger$i.info('Signing transaction', { options });
|
|
2677
2806
|
try {
|
|
2678
2807
|
const response = await this.channelService.signTransaction({
|
|
2679
2808
|
accountAddress: options.accountAddress,
|
|
@@ -2681,7 +2810,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2681
2810
|
ledgerId: options.ledgerId,
|
|
2682
2811
|
networkId: options.networkId,
|
|
2683
2812
|
});
|
|
2684
|
-
logger$
|
|
2813
|
+
logger$i.info('Transaction signed');
|
|
2685
2814
|
return {
|
|
2686
2815
|
signedPayload: response.signedPayload || '',
|
|
2687
2816
|
signature: response.signature,
|
|
@@ -2689,7 +2818,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2689
2818
|
};
|
|
2690
2819
|
}
|
|
2691
2820
|
catch (error) {
|
|
2692
|
-
logger$
|
|
2821
|
+
logger$i.error('Transaction signing failed', { error });
|
|
2693
2822
|
throw error;
|
|
2694
2823
|
}
|
|
2695
2824
|
}
|
|
@@ -2713,7 +2842,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2713
2842
|
* @throws {Error} If transaction submission fails
|
|
2714
2843
|
*/
|
|
2715
2844
|
async signAndExecuteTransaction(options) {
|
|
2716
|
-
logger$
|
|
2845
|
+
logger$i.info('signAndExecuteTransaction called (single prompt)', {
|
|
2717
2846
|
accountAddress: options.accountAddress,
|
|
2718
2847
|
ledgerId: options.ledgerId,
|
|
2719
2848
|
networkId: options.networkId,
|
|
@@ -2750,7 +2879,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2750
2879
|
* @param options
|
|
2751
2880
|
*/
|
|
2752
2881
|
async submitTransactionInternal(options) {
|
|
2753
|
-
logger$
|
|
2882
|
+
logger$i.info('Signing and submitting transaction (single prompt)', {
|
|
2754
2883
|
accountAddress: options.accountAddress,
|
|
2755
2884
|
ledgerId: options.ledgerId,
|
|
2756
2885
|
networkId: options.networkId,
|
|
@@ -2768,7 +2897,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2768
2897
|
innerTransactions: options.innerTransactions,
|
|
2769
2898
|
}),
|
|
2770
2899
|
});
|
|
2771
|
-
logger$
|
|
2900
|
+
logger$i.info('Transaction signed and submitted successfully', {
|
|
2772
2901
|
transactionId: response.transactionId,
|
|
2773
2902
|
transactionHash: response.transactionHash?.substring(0, 16),
|
|
2774
2903
|
});
|
|
@@ -2779,7 +2908,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2779
2908
|
};
|
|
2780
2909
|
}
|
|
2781
2910
|
catch (error) {
|
|
2782
|
-
logger$
|
|
2911
|
+
logger$i.error('Transaction sign+submit failed', { error });
|
|
2783
2912
|
throw error;
|
|
2784
2913
|
}
|
|
2785
2914
|
}
|
|
@@ -2801,7 +2930,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2801
2930
|
* @throws {Error} If wallet is not connected
|
|
2802
2931
|
*/
|
|
2803
2932
|
async signMessage(options) {
|
|
2804
|
-
logger$
|
|
2933
|
+
logger$i.info('Signing message', {
|
|
2805
2934
|
accountAddress: options.accountAddress,
|
|
2806
2935
|
encoding: options.encoding,
|
|
2807
2936
|
messageLength: options.message?.length,
|
|
@@ -2814,7 +2943,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2814
2943
|
ledgerId: options.ledgerId,
|
|
2815
2944
|
networkId: options.networkId,
|
|
2816
2945
|
});
|
|
2817
|
-
logger$
|
|
2946
|
+
logger$i.info('Message signed successfully', {
|
|
2818
2947
|
algorithm: response.algorithm,
|
|
2819
2948
|
caipChainId: response.caipChainId,
|
|
2820
2949
|
});
|
|
@@ -2831,7 +2960,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2831
2960
|
};
|
|
2832
2961
|
}
|
|
2833
2962
|
catch (error) {
|
|
2834
|
-
logger$
|
|
2963
|
+
logger$i.error('Message signing failed', { error });
|
|
2835
2964
|
throw error;
|
|
2836
2965
|
}
|
|
2837
2966
|
}
|
|
@@ -2863,7 +2992,7 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2863
2992
|
* @returns Promise resolving to true if reconnection succeeded, false otherwise
|
|
2864
2993
|
*/
|
|
2865
2994
|
async attemptReconnect() {
|
|
2866
|
-
logger$
|
|
2995
|
+
logger$i.info('Attempting reconnect via unified channel');
|
|
2867
2996
|
try {
|
|
2868
2997
|
const success = await this.channelService.attemptRestore();
|
|
2869
2998
|
if (success) {
|
|
@@ -2876,15 +3005,15 @@ class HsuiteNativeProvider extends BaseWalletProvider {
|
|
|
2876
3005
|
metadata: { autoReconnected: true },
|
|
2877
3006
|
timestamp: Date.now(),
|
|
2878
3007
|
});
|
|
2879
|
-
logger$
|
|
3008
|
+
logger$i.info('Reconnect successful');
|
|
2880
3009
|
}
|
|
2881
3010
|
else {
|
|
2882
|
-
logger$
|
|
3011
|
+
logger$i.warn('Reconnect failed - no session or unable to connect');
|
|
2883
3012
|
}
|
|
2884
3013
|
return success;
|
|
2885
3014
|
}
|
|
2886
3015
|
catch (error) {
|
|
2887
|
-
logger$
|
|
3016
|
+
logger$i.error('Reconnect failed', { error });
|
|
2888
3017
|
return false;
|
|
2889
3018
|
}
|
|
2890
3019
|
}
|
|
@@ -2919,7 +3048,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
2919
3048
|
* Implements the IWalletConnectSigner interface for Hedera ledger.
|
|
2920
3049
|
* Handles Hedera-specific JSON-RPC methods and transaction formats.
|
|
2921
3050
|
*/
|
|
2922
|
-
const logger$
|
|
3051
|
+
const logger$h = getLogger().scoped?.('HederaSigner') ?? getLogger();
|
|
2923
3052
|
/**
|
|
2924
3053
|
* Extract a meaningful error message from various error shapes.
|
|
2925
3054
|
* WalletConnect SDK throws errors that are not standard Error instances.
|
|
@@ -2937,7 +3066,7 @@ const logger$g = getLogger().scoped?.('HederaSigner') ?? getLogger();
|
|
|
2937
3066
|
*/
|
|
2938
3067
|
function extractErrorMessage$1(error) {
|
|
2939
3068
|
// Log the raw error for debugging
|
|
2940
|
-
logger$
|
|
3069
|
+
logger$h.debug('Extracting error message', {
|
|
2941
3070
|
errorType: typeof error,
|
|
2942
3071
|
isError: error instanceof Error,
|
|
2943
3072
|
constructorName: error?.constructor?.name,
|
|
@@ -2975,7 +3104,7 @@ function extractErrorMessage$1(error) {
|
|
|
2975
3104
|
propDebug[prop] = '[err]';
|
|
2976
3105
|
}
|
|
2977
3106
|
}
|
|
2978
|
-
logger$
|
|
3107
|
+
logger$h.error('WC Error object dump', propDebug);
|
|
2979
3108
|
// Check for code property first (common in SDK errors like PIN_REQUIRED)
|
|
2980
3109
|
if (typeof errorObj['code'] === 'string') {
|
|
2981
3110
|
const codeMsg = errorObj['message']
|
|
@@ -3056,7 +3185,7 @@ function extractErrorMessage$1(error) {
|
|
|
3056
3185
|
}
|
|
3057
3186
|
// Try getting own property names (includes non-enumerable)
|
|
3058
3187
|
const ownProps = Object.getOwnPropertyNames(errorObj);
|
|
3059
|
-
logger$
|
|
3188
|
+
logger$h.debug('Error object properties', { props: ownProps });
|
|
3060
3189
|
if (ownProps.length > 0) {
|
|
3061
3190
|
// Try to read ALL properties for better debugging
|
|
3062
3191
|
const propValues = [];
|
|
@@ -3077,7 +3206,7 @@ function extractErrorMessage$1(error) {
|
|
|
3077
3206
|
propValues.push(k);
|
|
3078
3207
|
}
|
|
3079
3208
|
}
|
|
3080
|
-
logger$
|
|
3209
|
+
logger$h.debug('Error property values', { values: propValues });
|
|
3081
3210
|
// Return a more informative error
|
|
3082
3211
|
return `Error with properties: ${propValues.slice(0, 5).join(', ')}`;
|
|
3083
3212
|
}
|
|
@@ -3090,7 +3219,7 @@ function extractErrorMessage$1(error) {
|
|
|
3090
3219
|
}
|
|
3091
3220
|
}
|
|
3092
3221
|
// Last resort - log what we know about the object
|
|
3093
|
-
logger$
|
|
3222
|
+
logger$h.warn('Could not extract error message', {
|
|
3094
3223
|
constructor: error?.constructor?.name,
|
|
3095
3224
|
prototype: Object.getPrototypeOf(error)?.constructor?.name,
|
|
3096
3225
|
});
|
|
@@ -3189,13 +3318,13 @@ class HederaSigner {
|
|
|
3189
3318
|
Object.keys(error).length === 0 &&
|
|
3190
3319
|
!(error instanceof Error);
|
|
3191
3320
|
if (isEmptyError) {
|
|
3192
|
-
logger$
|
|
3321
|
+
logger$h.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
3193
3322
|
topic: params.topic?.substring(0, 16) + '...',
|
|
3194
3323
|
});
|
|
3195
3324
|
throw new Error('WalletConnect connection issue - please try again');
|
|
3196
3325
|
}
|
|
3197
3326
|
const errorMsg = extractErrorMessage$1(error);
|
|
3198
|
-
logger$
|
|
3327
|
+
logger$h.error('Sign failed', { error: errorMsg });
|
|
3199
3328
|
throw new Error(`Hedera sign failed: ${errorMsg}`);
|
|
3200
3329
|
}
|
|
3201
3330
|
}
|
|
@@ -3211,7 +3340,7 @@ class HederaSigner {
|
|
|
3211
3340
|
const network = params.networkId.includes('mainnet') ? 'mainnet' : 'testnet';
|
|
3212
3341
|
// Format account ID in HIP-30 format
|
|
3213
3342
|
const signerAccountId = `hedera:${network}:${params.accountAddress}`;
|
|
3214
|
-
logger$
|
|
3343
|
+
logger$h.debug('Sending hedera_signAndExecuteTransaction request', {
|
|
3215
3344
|
topic: params.topic?.substring(0, 16) + '...',
|
|
3216
3345
|
chainId: `hedera:${network}`,
|
|
3217
3346
|
signerAccountId,
|
|
@@ -3239,7 +3368,7 @@ class HederaSigner {
|
|
|
3239
3368
|
try {
|
|
3240
3369
|
// Race between the request and our timeout
|
|
3241
3370
|
const result = await Promise.race([requestPromise, timeoutPromise]);
|
|
3242
|
-
logger$
|
|
3371
|
+
logger$h.debug('Received hedera_signAndExecuteTransaction response', {
|
|
3243
3372
|
hasResult: !!result,
|
|
3244
3373
|
resultKeys: result ? Object.keys(result) : [],
|
|
3245
3374
|
});
|
|
@@ -3272,7 +3401,7 @@ class HederaSigner {
|
|
|
3272
3401
|
// Get all own property names
|
|
3273
3402
|
ownProps: error && typeof error === 'object' ? Object.getOwnPropertyNames(error) : [],
|
|
3274
3403
|
};
|
|
3275
|
-
logger$
|
|
3404
|
+
logger$h.error('Submit failed - full error analysis', errorInfo);
|
|
3276
3405
|
// Check if this is an empty error object (WalletConnect SDK bug)
|
|
3277
3406
|
// This typically happens when relay subscription is stale
|
|
3278
3407
|
const isEmptyError = typeof error === 'object' &&
|
|
@@ -3284,7 +3413,7 @@ class HederaSigner {
|
|
|
3284
3413
|
// It usually means the relay subscription was stale
|
|
3285
3414
|
// The SessionHealthManager should have pinged before the request,
|
|
3286
3415
|
// but if we still get this error, the subscription dropped after the ping
|
|
3287
|
-
logger$
|
|
3416
|
+
logger$h.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
3288
3417
|
topic: params.topic?.substring(0, 16) + '...',
|
|
3289
3418
|
hint: 'This can happen when the relay WebSocket connection drops',
|
|
3290
3419
|
});
|
|
@@ -3294,7 +3423,7 @@ class HederaSigner {
|
|
|
3294
3423
|
'please check your transaction history as it may have been executed successfully.');
|
|
3295
3424
|
}
|
|
3296
3425
|
const errorMsg = extractErrorMessage$1(error);
|
|
3297
|
-
logger$
|
|
3426
|
+
logger$h.error('Submit failed', { error: errorMsg });
|
|
3298
3427
|
throw new Error(`Hedera submit failed: ${errorMsg}`);
|
|
3299
3428
|
}
|
|
3300
3429
|
}
|
|
@@ -3351,13 +3480,13 @@ class HederaSigner {
|
|
|
3351
3480
|
Object.keys(error).length === 0 &&
|
|
3352
3481
|
!(error instanceof Error);
|
|
3353
3482
|
if (isEmptyError) {
|
|
3354
|
-
logger$
|
|
3483
|
+
logger$h.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
3355
3484
|
topic: params.topic?.substring(0, 16) + '...',
|
|
3356
3485
|
});
|
|
3357
3486
|
throw new Error('WalletConnect connection issue - please try again');
|
|
3358
3487
|
}
|
|
3359
3488
|
const errorMsg = extractErrorMessage$1(error);
|
|
3360
|
-
logger$
|
|
3489
|
+
logger$h.error('Sign message failed', { error: errorMsg });
|
|
3361
3490
|
throw new Error(`Hedera sign message failed: ${errorMsg}`);
|
|
3362
3491
|
}
|
|
3363
3492
|
}
|
|
@@ -3375,15 +3504,15 @@ class HederaSigner {
|
|
|
3375
3504
|
*/
|
|
3376
3505
|
parseAccounts(namespace, targetNetworkId) {
|
|
3377
3506
|
if (!namespace) {
|
|
3378
|
-
logger$
|
|
3507
|
+
logger$h.warn('Namespace is null or undefined');
|
|
3379
3508
|
return [];
|
|
3380
3509
|
}
|
|
3381
3510
|
if (!namespace.accounts) {
|
|
3382
|
-
logger$
|
|
3511
|
+
logger$h.warn('Namespace has no accounts property');
|
|
3383
3512
|
return [];
|
|
3384
3513
|
}
|
|
3385
3514
|
if (!Array.isArray(namespace.accounts)) {
|
|
3386
|
-
logger$
|
|
3515
|
+
logger$h.warn('Namespace accounts is not an array', {
|
|
3387
3516
|
type: typeof namespace.accounts,
|
|
3388
3517
|
});
|
|
3389
3518
|
return [];
|
|
@@ -3393,7 +3522,7 @@ class HederaSigner {
|
|
|
3393
3522
|
// HIP-30 format: hedera:testnet:0.0.12345
|
|
3394
3523
|
const parts = account.split(':');
|
|
3395
3524
|
if (parts.length < 3) {
|
|
3396
|
-
logger$
|
|
3525
|
+
logger$h.warn('Invalid account format', { account, partsLength: parts.length });
|
|
3397
3526
|
return { address: account, chainId: 'hedera:testnet' };
|
|
3398
3527
|
}
|
|
3399
3528
|
return {
|
|
@@ -3406,7 +3535,7 @@ class HederaSigner {
|
|
|
3406
3535
|
const targetNetwork = targetNetworkId.split(':')[1]?.toLowerCase(); // e.g., 'mainnet'
|
|
3407
3536
|
if (targetNetwork) {
|
|
3408
3537
|
const filteredAccounts = allAccounts.filter((acc) => acc.chainId.toLowerCase().includes(targetNetwork));
|
|
3409
|
-
logger$
|
|
3538
|
+
logger$h.debug('Filtered accounts by target network', {
|
|
3410
3539
|
targetNetworkId,
|
|
3411
3540
|
targetNetwork,
|
|
3412
3541
|
totalAccounts: allAccounts.length,
|
|
@@ -3414,7 +3543,7 @@ class HederaSigner {
|
|
|
3414
3543
|
});
|
|
3415
3544
|
// If no accounts match the target network, log a warning
|
|
3416
3545
|
if (filteredAccounts.length === 0 && allAccounts.length > 0) {
|
|
3417
|
-
logger$
|
|
3546
|
+
logger$h.warn('No accounts found for target network', {
|
|
3418
3547
|
targetNetwork,
|
|
3419
3548
|
availableNetworks: [...new Set(allAccounts.map((a) => a.chainId))],
|
|
3420
3549
|
});
|
|
@@ -3442,7 +3571,7 @@ class HederaSigner {
|
|
|
3442
3571
|
* Implements the IWalletConnectSigner interface for XRPL ledger.
|
|
3443
3572
|
* Handles XRPL-specific JSON-RPC methods and transaction formats.
|
|
3444
3573
|
*/
|
|
3445
|
-
const logger$
|
|
3574
|
+
const logger$g = getLogger().scoped?.('XrplSigner') ?? getLogger();
|
|
3446
3575
|
/**
|
|
3447
3576
|
* Extract a meaningful error message from various error shapes.
|
|
3448
3577
|
* WalletConnect SDK throws errors that are not standard Error instances.
|
|
@@ -3460,7 +3589,7 @@ const logger$f = getLogger().scoped?.('XrplSigner') ?? getLogger();
|
|
|
3460
3589
|
*/
|
|
3461
3590
|
function extractErrorMessage(error) {
|
|
3462
3591
|
// Log raw error for debugging
|
|
3463
|
-
logger$
|
|
3592
|
+
logger$g.debug('Raw error object', {
|
|
3464
3593
|
errorType: typeof error,
|
|
3465
3594
|
isError: error instanceof Error,
|
|
3466
3595
|
errorKeys: typeof error === 'object' && error !== null ? Object.keys(error) : [],
|
|
@@ -3596,7 +3725,7 @@ class XrplSigner {
|
|
|
3596
3725
|
*/
|
|
3597
3726
|
async signTransaction(params) {
|
|
3598
3727
|
// Decode base64 payload to JSON transaction
|
|
3599
|
-
logger$
|
|
3728
|
+
logger$g.debug('Decoding XRPL transaction payload', {
|
|
3600
3729
|
payloadLength: params.payload?.length,
|
|
3601
3730
|
networkId: params.networkId,
|
|
3602
3731
|
topic: params.topic?.substring(0, 16) + '...',
|
|
@@ -3604,19 +3733,19 @@ class XrplSigner {
|
|
|
3604
3733
|
let transaction;
|
|
3605
3734
|
try {
|
|
3606
3735
|
transaction = XrplTransactionEncoder.decodeTransaction(params.payload);
|
|
3607
|
-
logger$
|
|
3736
|
+
logger$g.debug('Transaction decoded successfully', {
|
|
3608
3737
|
transactionType: transaction?.TransactionType,
|
|
3609
3738
|
account: transaction?.Account,
|
|
3610
3739
|
});
|
|
3611
3740
|
}
|
|
3612
3741
|
catch (decodeError) {
|
|
3613
|
-
logger$
|
|
3742
|
+
logger$g.error('Failed to decode transaction payload', {
|
|
3614
3743
|
error: decodeError instanceof Error ? decodeError.message : String(decodeError),
|
|
3615
3744
|
});
|
|
3616
3745
|
throw decodeError;
|
|
3617
3746
|
}
|
|
3618
3747
|
try {
|
|
3619
|
-
logger$
|
|
3748
|
+
logger$g.debug('Sending xrpl_signTransaction request to wallet', {
|
|
3620
3749
|
method: 'xrpl_signTransaction',
|
|
3621
3750
|
chainId: params.networkId,
|
|
3622
3751
|
topic: params.topic?.substring(0, 16) + '...',
|
|
@@ -3633,7 +3762,7 @@ class XrplSigner {
|
|
|
3633
3762
|
},
|
|
3634
3763
|
expiry: 300, // 5 minutes in seconds
|
|
3635
3764
|
});
|
|
3636
|
-
logger$
|
|
3765
|
+
logger$g.debug('Received response from wallet', {
|
|
3637
3766
|
hasResult: !!result,
|
|
3638
3767
|
resultKeys: result ? Object.keys(result) : [],
|
|
3639
3768
|
});
|
|
@@ -3649,13 +3778,13 @@ class XrplSigner {
|
|
|
3649
3778
|
Object.keys(error).length === 0 &&
|
|
3650
3779
|
!(error instanceof Error);
|
|
3651
3780
|
if (isEmptyError) {
|
|
3652
|
-
logger$
|
|
3781
|
+
logger$g.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
3653
3782
|
topic: params.topic?.substring(0, 16) + '...',
|
|
3654
3783
|
});
|
|
3655
3784
|
throw new Error('WalletConnect connection issue - please try again');
|
|
3656
3785
|
}
|
|
3657
3786
|
const errorMsg = extractErrorMessage(error);
|
|
3658
|
-
logger$
|
|
3787
|
+
logger$g.error('Sign failed', { error: errorMsg });
|
|
3659
3788
|
throw new Error(`XRPL sign failed: ${errorMsg}`);
|
|
3660
3789
|
}
|
|
3661
3790
|
}
|
|
@@ -3670,7 +3799,7 @@ class XrplSigner {
|
|
|
3670
3799
|
// Decode base64 payload to JSON transaction
|
|
3671
3800
|
const transaction = XrplTransactionEncoder.decodeTransaction(params.payload);
|
|
3672
3801
|
try {
|
|
3673
|
-
logger$
|
|
3802
|
+
logger$g.debug('Sending xrpl_signAndSubmit request to wallet', {
|
|
3674
3803
|
method: 'xrpl_signAndSubmit',
|
|
3675
3804
|
chainId: params.networkId,
|
|
3676
3805
|
topic: params.topic?.substring(0, 16) + '...',
|
|
@@ -3687,7 +3816,7 @@ class XrplSigner {
|
|
|
3687
3816
|
},
|
|
3688
3817
|
expiry: 300, // 5 minutes in seconds
|
|
3689
3818
|
});
|
|
3690
|
-
logger$
|
|
3819
|
+
logger$g.debug('Received xrpl_signAndSubmit response', {
|
|
3691
3820
|
hasResult: !!result,
|
|
3692
3821
|
resultKeys: result ? Object.keys(result) : [],
|
|
3693
3822
|
});
|
|
@@ -3709,14 +3838,14 @@ class XrplSigner {
|
|
|
3709
3838
|
if (isEmptyError) {
|
|
3710
3839
|
// Empty error object is a known WalletConnect SDK issue
|
|
3711
3840
|
// It usually means the relay subscription was stale
|
|
3712
|
-
logger$
|
|
3841
|
+
logger$g.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
3713
3842
|
topic: params.topic?.substring(0, 16) + '...',
|
|
3714
3843
|
hint: 'This can happen when the relay WebSocket connection drops',
|
|
3715
3844
|
});
|
|
3716
3845
|
throw new Error('WalletConnect connection issue - please try again');
|
|
3717
3846
|
}
|
|
3718
3847
|
const errorMsg = extractErrorMessage(error);
|
|
3719
|
-
logger$
|
|
3848
|
+
logger$g.error('Submit failed', { error: errorMsg });
|
|
3720
3849
|
throw new Error(`XRPL submit failed: ${errorMsg}`);
|
|
3721
3850
|
}
|
|
3722
3851
|
}
|
|
@@ -3766,13 +3895,13 @@ class XrplSigner {
|
|
|
3766
3895
|
Object.keys(error).length === 0 &&
|
|
3767
3896
|
!(error instanceof Error);
|
|
3768
3897
|
if (isEmptyError) {
|
|
3769
|
-
logger$
|
|
3898
|
+
logger$g.warn('WalletConnect SDK empty error - relay subscription may have been stale', {
|
|
3770
3899
|
topic: params.topic?.substring(0, 16) + '...',
|
|
3771
3900
|
});
|
|
3772
3901
|
throw new Error('WalletConnect connection issue - please try again');
|
|
3773
3902
|
}
|
|
3774
3903
|
const errorMsg = extractErrorMessage(error);
|
|
3775
|
-
logger$
|
|
3904
|
+
logger$g.error('Sign message failed', { error: errorMsg });
|
|
3776
3905
|
throw new Error(`XRPL sign message failed: ${errorMsg}`);
|
|
3777
3906
|
}
|
|
3778
3907
|
}
|
|
@@ -3797,7 +3926,7 @@ class XrplSigner {
|
|
|
3797
3926
|
// XRPL format: xrpl:testnet:rN7n7otQDd6FczFgLdlqtyMVrn3WnFBrDB
|
|
3798
3927
|
const parts = account.split(':');
|
|
3799
3928
|
if (parts.length < 3) {
|
|
3800
|
-
logger$
|
|
3929
|
+
logger$g.warn('Invalid account format', { account });
|
|
3801
3930
|
return { address: account, chainId: 'xrpl:testnet' };
|
|
3802
3931
|
}
|
|
3803
3932
|
return {
|
|
@@ -3810,7 +3939,7 @@ class XrplSigner {
|
|
|
3810
3939
|
const targetNetwork = targetNetworkId.split(':')[1]?.toLowerCase(); // e.g., 'mainnet'
|
|
3811
3940
|
if (targetNetwork) {
|
|
3812
3941
|
const filteredAccounts = allAccounts.filter((acc) => acc.chainId.toLowerCase().includes(targetNetwork));
|
|
3813
|
-
logger$
|
|
3942
|
+
logger$g.debug('Filtered accounts by target network', {
|
|
3814
3943
|
targetNetworkId,
|
|
3815
3944
|
targetNetwork,
|
|
3816
3945
|
totalAccounts: allAccounts.length,
|
|
@@ -3818,7 +3947,7 @@ class XrplSigner {
|
|
|
3818
3947
|
});
|
|
3819
3948
|
// If no accounts match the target network, log a warning
|
|
3820
3949
|
if (filteredAccounts.length === 0 && allAccounts.length > 0) {
|
|
3821
|
-
logger$
|
|
3950
|
+
logger$g.warn('No accounts found for target network', {
|
|
3822
3951
|
targetNetwork,
|
|
3823
3952
|
availableNetworks: [...new Set(allAccounts.map((a) => a.chainId))],
|
|
3824
3953
|
});
|
|
@@ -3850,7 +3979,7 @@ class XrplSigner {
|
|
|
3850
3979
|
* 1. Implementing IWalletConnectSigner interface
|
|
3851
3980
|
* 2. Registering the signer with SignerFactory
|
|
3852
3981
|
*/
|
|
3853
|
-
const logger$
|
|
3982
|
+
const logger$f = getLogger().scoped?.('SignerFactory') ?? getLogger();
|
|
3854
3983
|
/**
|
|
3855
3984
|
* Factory class for creating and managing WalletConnect signers.
|
|
3856
3985
|
*
|
|
@@ -3892,13 +4021,13 @@ class SignerFactory {
|
|
|
3892
4021
|
*/
|
|
3893
4022
|
static registerSigner(ledgerId, signer) {
|
|
3894
4023
|
if (signer.ledgerId !== ledgerId) {
|
|
3895
|
-
logger$
|
|
4024
|
+
logger$f.warn('Signer ledgerId does not match registration key', {
|
|
3896
4025
|
signerLedgerId: signer.ledgerId,
|
|
3897
4026
|
registrationKey: ledgerId,
|
|
3898
4027
|
});
|
|
3899
4028
|
}
|
|
3900
4029
|
this.signers.set(ledgerId, signer);
|
|
3901
|
-
logger$
|
|
4030
|
+
logger$f.debug('Registered signer for ledger', { ledgerId });
|
|
3902
4031
|
}
|
|
3903
4032
|
/**
|
|
3904
4033
|
* Check if a signer is registered for a ledger.
|
|
@@ -3927,7 +4056,7 @@ class SignerFactory {
|
|
|
3927
4056
|
static unregisterSigner(ledgerId) {
|
|
3928
4057
|
const removed = this.signers.delete(ledgerId);
|
|
3929
4058
|
if (removed) {
|
|
3930
|
-
logger$
|
|
4059
|
+
logger$f.debug('Unregistered signer for ledger', { ledgerId });
|
|
3931
4060
|
}
|
|
3932
4061
|
return removed;
|
|
3933
4062
|
}
|
|
@@ -3956,7 +4085,7 @@ class SignerFactory {
|
|
|
3956
4085
|
* - Automatic ping cache management
|
|
3957
4086
|
* - Subscription refresh capability
|
|
3958
4087
|
*/
|
|
3959
|
-
const logger$
|
|
4088
|
+
const logger$e = getLogger().scoped?.('WCSessionHealth') ?? getLogger();
|
|
3960
4089
|
/**
|
|
3961
4090
|
* Manages WalletConnect session health to prevent stale relay subscription issues.
|
|
3962
4091
|
*
|
|
@@ -3987,7 +4116,7 @@ class SessionHealthManager {
|
|
|
3987
4116
|
constructor(client) {
|
|
3988
4117
|
this.client = client;
|
|
3989
4118
|
this.setupRelayMonitoring();
|
|
3990
|
-
logger$
|
|
4119
|
+
logger$e.info('SessionHealthManager initialized');
|
|
3991
4120
|
}
|
|
3992
4121
|
/**
|
|
3993
4122
|
* Ensure session is healthy before making a request.
|
|
@@ -4005,14 +4134,14 @@ class SessionHealthManager {
|
|
|
4005
4134
|
const now = Date.now();
|
|
4006
4135
|
// Skip if we pinged recently
|
|
4007
4136
|
if (now - lastPing <= this.PING_INTERVAL_MS) {
|
|
4008
|
-
logger$
|
|
4137
|
+
logger$e.debug('Skipping ping - recent ping exists', {
|
|
4009
4138
|
topic: topic.substring(0, 16) + '...',
|
|
4010
4139
|
lastPingAge: now - lastPing,
|
|
4011
4140
|
});
|
|
4012
4141
|
return;
|
|
4013
4142
|
}
|
|
4014
4143
|
try {
|
|
4015
|
-
logger$
|
|
4144
|
+
logger$e.debug('Pinging session to ensure relay subscription', {
|
|
4016
4145
|
topic: topic.substring(0, 16) + '...',
|
|
4017
4146
|
});
|
|
4018
4147
|
// Race ping against timeout
|
|
@@ -4021,14 +4150,14 @@ class SessionHealthManager {
|
|
|
4021
4150
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Ping timeout')), this.PING_TIMEOUT_MS)),
|
|
4022
4151
|
]);
|
|
4023
4152
|
this.lastPingTime.set(topic, now);
|
|
4024
|
-
logger$
|
|
4153
|
+
logger$e.debug('Session ping successful', {
|
|
4025
4154
|
topic: topic.substring(0, 16) + '...',
|
|
4026
4155
|
});
|
|
4027
4156
|
}
|
|
4028
4157
|
catch (err) {
|
|
4029
4158
|
// Log warning but don't throw - let the request attempt proceed
|
|
4030
4159
|
// The request might still succeed if the subscription is actually active
|
|
4031
|
-
logger$
|
|
4160
|
+
logger$e.warn('Session ping failed - relay subscription may be stale', {
|
|
4032
4161
|
topic: topic.substring(0, 16) + '...',
|
|
4033
4162
|
error: err instanceof Error ? err.message : String(err),
|
|
4034
4163
|
});
|
|
@@ -4050,16 +4179,16 @@ class SessionHealthManager {
|
|
|
4050
4179
|
await relayer.subscribe(topic);
|
|
4051
4180
|
// Clear ping cache so next ensureSessionHealth will ping
|
|
4052
4181
|
this.lastPingTime.delete(topic);
|
|
4053
|
-
logger$
|
|
4182
|
+
logger$e.info('Refreshed relay subscription', {
|
|
4054
4183
|
topic: topic.substring(0, 16) + '...',
|
|
4055
4184
|
});
|
|
4056
4185
|
}
|
|
4057
4186
|
else {
|
|
4058
|
-
logger$
|
|
4187
|
+
logger$e.warn('Relayer.subscribe not available');
|
|
4059
4188
|
}
|
|
4060
4189
|
}
|
|
4061
4190
|
catch (err) {
|
|
4062
|
-
logger$
|
|
4191
|
+
logger$e.warn('Failed to refresh subscription', {
|
|
4063
4192
|
topic: topic.substring(0, 16) + '...',
|
|
4064
4193
|
error: err instanceof Error ? err.message : String(err),
|
|
4065
4194
|
});
|
|
@@ -4092,22 +4221,22 @@ class SessionHealthManager {
|
|
|
4092
4221
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- WalletConnect internal API not typed
|
|
4093
4222
|
const relayer = this.client.core?.relayer;
|
|
4094
4223
|
if (!relayer) {
|
|
4095
|
-
logger$
|
|
4224
|
+
logger$e.warn('Could not access relayer for monitoring');
|
|
4096
4225
|
return;
|
|
4097
4226
|
}
|
|
4098
4227
|
relayer.on('connect', () => {
|
|
4099
|
-
logger$
|
|
4228
|
+
logger$e.info('Relay WebSocket connected');
|
|
4100
4229
|
});
|
|
4101
4230
|
relayer.on('disconnect', () => {
|
|
4102
|
-
logger$
|
|
4231
|
+
logger$e.warn('Relay WebSocket disconnected - clearing ping cache');
|
|
4103
4232
|
this.lastPingTime.clear();
|
|
4104
4233
|
});
|
|
4105
4234
|
relayer.on('error', (err) => {
|
|
4106
|
-
logger$
|
|
4235
|
+
logger$e.error('Relay error', {
|
|
4107
4236
|
error: err?.message || String(err),
|
|
4108
4237
|
});
|
|
4109
4238
|
});
|
|
4110
|
-
logger$
|
|
4239
|
+
logger$e.debug('Relay monitoring setup complete');
|
|
4111
4240
|
}
|
|
4112
4241
|
}
|
|
4113
4242
|
|
|
@@ -4132,7 +4261,7 @@ class SessionHealthManager {
|
|
|
4132
4261
|
*
|
|
4133
4262
|
* Extracted from WalletConnectV2Provider to reduce file size.
|
|
4134
4263
|
*/
|
|
4135
|
-
const logger$
|
|
4264
|
+
const logger$d = getLogger().scoped?.('WCClientManager') ?? getLogger();
|
|
4136
4265
|
/**
|
|
4137
4266
|
* Storage key for persisted projectId
|
|
4138
4267
|
*/
|
|
@@ -4159,7 +4288,7 @@ class WalletConnectClientManager {
|
|
|
4159
4288
|
if (this.client && this.projectId === projectId) {
|
|
4160
4289
|
return this.client;
|
|
4161
4290
|
}
|
|
4162
|
-
logger$
|
|
4291
|
+
logger$d.debug('Initializing SignClient', { projectId: projectId.substring(0, 8) });
|
|
4163
4292
|
// Store projectId for session restoration
|
|
4164
4293
|
this.projectId = projectId;
|
|
4165
4294
|
this.persistProjectId(projectId);
|
|
@@ -4171,7 +4300,7 @@ class WalletConnectClientManager {
|
|
|
4171
4300
|
});
|
|
4172
4301
|
// Initialize session health manager for relay subscription management
|
|
4173
4302
|
this.sessionHealth = new SessionHealthManager(this.client);
|
|
4174
|
-
logger$
|
|
4303
|
+
logger$d.info('SignClient initialized with session health manager');
|
|
4175
4304
|
return this.client;
|
|
4176
4305
|
}
|
|
4177
4306
|
/**
|
|
@@ -4200,14 +4329,14 @@ class WalletConnectClientManager {
|
|
|
4200
4329
|
async restoreClient(metadata) {
|
|
4201
4330
|
const storedProjectId = this.getStoredProjectId();
|
|
4202
4331
|
if (!storedProjectId) {
|
|
4203
|
-
logger$
|
|
4332
|
+
logger$d.debug('No stored projectId, skipping client restoration');
|
|
4204
4333
|
return undefined;
|
|
4205
4334
|
}
|
|
4206
4335
|
try {
|
|
4207
4336
|
return await this.initialize(storedProjectId, metadata);
|
|
4208
4337
|
}
|
|
4209
4338
|
catch (error) {
|
|
4210
|
-
logger$
|
|
4339
|
+
logger$d.error('Failed to restore client', {
|
|
4211
4340
|
error: error instanceof Error ? error.message : String(error),
|
|
4212
4341
|
});
|
|
4213
4342
|
return undefined;
|
|
@@ -4274,7 +4403,7 @@ class WalletConnectClientManager {
|
|
|
4274
4403
|
if (sessionProperties) {
|
|
4275
4404
|
connectParams.sessionProperties = sessionProperties;
|
|
4276
4405
|
}
|
|
4277
|
-
logger$
|
|
4406
|
+
logger$d.debug('Connecting with params', {
|
|
4278
4407
|
namespaces: Object.keys(optionalNamespaces),
|
|
4279
4408
|
hasSessionProperties: !!sessionProperties,
|
|
4280
4409
|
});
|
|
@@ -4340,7 +4469,7 @@ class WalletConnectClientManager {
|
|
|
4340
4469
|
const startKeepAlive = () => {
|
|
4341
4470
|
keepAliveInterval = setInterval(async () => {
|
|
4342
4471
|
try {
|
|
4343
|
-
logger$
|
|
4472
|
+
logger$d.debug('Keep-alive ping during request', {
|
|
4344
4473
|
topic: params.topic?.substring(0, 16) + '...',
|
|
4345
4474
|
method: params.request.method,
|
|
4346
4475
|
});
|
|
@@ -4353,7 +4482,7 @@ class WalletConnectClientManager {
|
|
|
4353
4482
|
}
|
|
4354
4483
|
catch (err) {
|
|
4355
4484
|
// Log but don't throw - ping failure during wait is not critical
|
|
4356
|
-
logger$
|
|
4485
|
+
logger$d.debug('Keep-alive ping failed (non-critical)', {
|
|
4357
4486
|
error: err instanceof Error ? err.message : String(err),
|
|
4358
4487
|
});
|
|
4359
4488
|
}
|
|
@@ -4395,7 +4524,7 @@ class WalletConnectClientManager {
|
|
|
4395
4524
|
window.localStorage?.setItem(STORAGE_KEY_PROJECT_ID, projectId);
|
|
4396
4525
|
}
|
|
4397
4526
|
catch {
|
|
4398
|
-
logger$
|
|
4527
|
+
logger$d.warn('Failed to persist projectId');
|
|
4399
4528
|
}
|
|
4400
4529
|
}
|
|
4401
4530
|
getStoredProjectId() {
|
|
@@ -4440,7 +4569,7 @@ class WalletConnectClientManager {
|
|
|
4440
4569
|
* - Aggregate accounts across sessions
|
|
4441
4570
|
* - Find sessions by account address
|
|
4442
4571
|
*/
|
|
4443
|
-
const logger$
|
|
4572
|
+
const logger$c = getLogger().scoped?.('WCSessionStore') ?? getLogger();
|
|
4444
4573
|
/**
|
|
4445
4574
|
* WalletConnect Session Store
|
|
4446
4575
|
*
|
|
@@ -4478,7 +4607,7 @@ class WalletConnectSessionStore {
|
|
|
4478
4607
|
*/
|
|
4479
4608
|
setSession(sessionKey, data) {
|
|
4480
4609
|
this.sessions.set(sessionKey, data);
|
|
4481
|
-
logger$
|
|
4610
|
+
logger$c.debug('Session stored', { sessionKey, accountCount: data.accounts.length });
|
|
4482
4611
|
}
|
|
4483
4612
|
/**
|
|
4484
4613
|
* Delete a session by key.
|
|
@@ -4489,7 +4618,7 @@ class WalletConnectSessionStore {
|
|
|
4489
4618
|
deleteSession(sessionKey) {
|
|
4490
4619
|
const deleted = this.sessions.delete(sessionKey);
|
|
4491
4620
|
if (deleted) {
|
|
4492
|
-
logger$
|
|
4621
|
+
logger$c.debug('Session deleted', { sessionKey });
|
|
4493
4622
|
}
|
|
4494
4623
|
return deleted;
|
|
4495
4624
|
}
|
|
@@ -4513,7 +4642,7 @@ class WalletConnectSessionStore {
|
|
|
4513
4642
|
clear() {
|
|
4514
4643
|
this.sessions.clear();
|
|
4515
4644
|
this._accounts.set([]);
|
|
4516
|
-
logger$
|
|
4645
|
+
logger$c.debug('All sessions cleared');
|
|
4517
4646
|
}
|
|
4518
4647
|
/**
|
|
4519
4648
|
* Get all session entries.
|
|
@@ -4554,7 +4683,7 @@ class WalletConnectSessionStore {
|
|
|
4554
4683
|
for (const sessionData of this.sessions.values()) {
|
|
4555
4684
|
allAccounts.push(...sessionData.accounts);
|
|
4556
4685
|
}
|
|
4557
|
-
logger$
|
|
4686
|
+
logger$c.debug('Aggregated accounts', {
|
|
4558
4687
|
sessionCount: this.sessions.size,
|
|
4559
4688
|
totalAccounts: allAccounts.length,
|
|
4560
4689
|
});
|
|
@@ -4590,7 +4719,7 @@ class WalletConnectSessionStore {
|
|
|
4590
4719
|
for (const [sessionKey, sessionData] of this.sessions.entries()) {
|
|
4591
4720
|
if (sessionData.session.topic === topic) {
|
|
4592
4721
|
this.sessions.delete(sessionKey);
|
|
4593
|
-
logger$
|
|
4722
|
+
logger$c.info('Session removed by topic', { sessionKey, topic: topic.substring(0, 16) });
|
|
4594
4723
|
return { sessionKey, data: sessionData };
|
|
4595
4724
|
}
|
|
4596
4725
|
}
|
|
@@ -4638,7 +4767,7 @@ class WalletConnectSessionStore {
|
|
|
4638
4767
|
*
|
|
4639
4768
|
* Extracted from WalletConnectV2Provider to reduce file size.
|
|
4640
4769
|
*/
|
|
4641
|
-
const logger$
|
|
4770
|
+
const logger$b = getLogger().scoped?.('WCSigningOrchestrator') ?? getLogger();
|
|
4642
4771
|
/**
|
|
4643
4772
|
* Signing Orchestrator
|
|
4644
4773
|
*
|
|
@@ -4669,7 +4798,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4669
4798
|
const sessionInfo = this.findAndValidateSession(options.accountAddress, 'signTransaction');
|
|
4670
4799
|
// Use user's selected network from session, with fallback to options.networkId
|
|
4671
4800
|
const networkId = this.resolveNetworkId(sessionInfo, options.networkId);
|
|
4672
|
-
logger$
|
|
4801
|
+
logger$b.debug('Routing sign to signer', {
|
|
4673
4802
|
ledgerId: sessionInfo.signer.ledgerId,
|
|
4674
4803
|
topic: sessionInfo.session.topic.substring(0, 16) + '...',
|
|
4675
4804
|
networkId,
|
|
@@ -4692,7 +4821,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4692
4821
|
const sessionInfo = this.findAndValidateSession(options.accountAddress, 'submitTransaction');
|
|
4693
4822
|
// Use user's selected network from session, with fallback to options.networkId
|
|
4694
4823
|
const networkId = this.resolveNetworkId(sessionInfo, options.networkId);
|
|
4695
|
-
logger$
|
|
4824
|
+
logger$b.debug('Routing submit to signer', {
|
|
4696
4825
|
ledgerId: sessionInfo.signer.ledgerId,
|
|
4697
4826
|
topic: sessionInfo.session.topic.substring(0, 16) + '...',
|
|
4698
4827
|
networkId,
|
|
@@ -4715,7 +4844,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4715
4844
|
const sessionInfo = this.findAndValidateSession(options.accountAddress, 'signAndExecuteTransaction');
|
|
4716
4845
|
// Use user's selected network from session, with fallback to options.networkId
|
|
4717
4846
|
const networkId = this.resolveNetworkId(sessionInfo, options.networkId);
|
|
4718
|
-
logger$
|
|
4847
|
+
logger$b.debug('Routing signAndExecute to signer', {
|
|
4719
4848
|
ledgerId: sessionInfo.signer.ledgerId,
|
|
4720
4849
|
topic: sessionInfo.session.topic.substring(0, 16) + '...',
|
|
4721
4850
|
networkId,
|
|
@@ -4756,7 +4885,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4756
4885
|
const sessionInfo = this.findAndValidateSession(options.accountAddress, 'signMessage');
|
|
4757
4886
|
// Use user's selected network from session, with fallback to options.networkId
|
|
4758
4887
|
const networkId = this.resolveNetworkId(sessionInfo, options.networkId);
|
|
4759
|
-
logger$
|
|
4888
|
+
logger$b.debug('Routing signMessage to signer', {
|
|
4760
4889
|
ledgerId: sessionInfo.signer.ledgerId,
|
|
4761
4890
|
topic: sessionInfo.session.topic.substring(0, 16) + '...',
|
|
4762
4891
|
networkId,
|
|
@@ -4797,7 +4926,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4797
4926
|
if (optionsNetworkId &&
|
|
4798
4927
|
sessionInfo.userSelectedNetwork &&
|
|
4799
4928
|
optionsNetworkId !== sessionInfo.userSelectedNetwork) {
|
|
4800
|
-
logger$
|
|
4929
|
+
logger$b.warn('Network mismatch: using session network over caller-provided network', {
|
|
4801
4930
|
callerNetwork: optionsNetworkId,
|
|
4802
4931
|
sessionNetwork: sessionInfo.userSelectedNetwork,
|
|
4803
4932
|
using: resolvedNetwork,
|
|
@@ -4817,7 +4946,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4817
4946
|
}
|
|
4818
4947
|
// Validate session exists in WC SDK
|
|
4819
4948
|
if (!this.validateSessionInSdk(sessionInfo.session.topic)) {
|
|
4820
|
-
logger$
|
|
4949
|
+
logger$b.error('Session not found in WC SDK', {
|
|
4821
4950
|
topic: sessionInfo.session.topic.substring(0, 16) + '...',
|
|
4822
4951
|
account: accountAddress,
|
|
4823
4952
|
});
|
|
@@ -4844,7 +4973,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4844
4973
|
return !!session;
|
|
4845
4974
|
}
|
|
4846
4975
|
catch (error) {
|
|
4847
|
-
logger$
|
|
4976
|
+
logger$b.warn('Session validation failed', {
|
|
4848
4977
|
topic: topic.substring(0, 16) + '...',
|
|
4849
4978
|
error: error instanceof Error ? error.message : String(error),
|
|
4850
4979
|
});
|
|
@@ -4880,7 +5009,7 @@ class WalletConnectSigningOrchestrator {
|
|
|
4880
5009
|
* - Testable (mock signers for unit tests)
|
|
4881
5010
|
* - Maintainable (ledger logic is isolated)
|
|
4882
5011
|
*/
|
|
4883
|
-
const logger$
|
|
5012
|
+
const logger$a = getLogger().scoped?.('WalletConnectV2Provider') ?? getLogger();
|
|
4884
5013
|
/**
|
|
4885
5014
|
* Storage key prefix for persisting user's network selection per session topic.
|
|
4886
5015
|
* This is critical for proper session restoration after page reload.
|
|
@@ -4944,7 +5073,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
4944
5073
|
clientManager: this.clientManager,
|
|
4945
5074
|
eventBus: this.eventBus,
|
|
4946
5075
|
});
|
|
4947
|
-
logger$
|
|
5076
|
+
logger$a.info('Initializing - will attempt session restoration');
|
|
4948
5077
|
void this.restoreSession();
|
|
4949
5078
|
}
|
|
4950
5079
|
/**
|
|
@@ -4971,7 +5100,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
4971
5100
|
*/
|
|
4972
5101
|
async restoreSession() {
|
|
4973
5102
|
try {
|
|
4974
|
-
logger$
|
|
5103
|
+
logger$a.debug('Attempting to restore sessions...');
|
|
4975
5104
|
// Restore client from stored projectId
|
|
4976
5105
|
const client = await this.clientManager.restoreClient({
|
|
4977
5106
|
name: 'HSuite Demo',
|
|
@@ -4986,7 +5115,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
4986
5115
|
this.registerSessionDeleteListener();
|
|
4987
5116
|
// Get all active sessions
|
|
4988
5117
|
const activeSessions = this.clientManager.getActiveSessions();
|
|
4989
|
-
logger$
|
|
5118
|
+
logger$a.info('Found active sessions', { count: activeSessions.length });
|
|
4990
5119
|
if (activeSessions.length === 0) {
|
|
4991
5120
|
return;
|
|
4992
5121
|
}
|
|
@@ -4996,7 +5125,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
4996
5125
|
await this.restoreSingleSession(session);
|
|
4997
5126
|
}
|
|
4998
5127
|
catch (error) {
|
|
4999
|
-
logger$
|
|
5128
|
+
logger$a.error('Failed to restore session', {
|
|
5000
5129
|
topic: session.topic,
|
|
5001
5130
|
error: error instanceof Error ? error.message : String(error),
|
|
5002
5131
|
});
|
|
@@ -5006,11 +5135,11 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5006
5135
|
this.sessionStore.updateAggregatedAccounts();
|
|
5007
5136
|
if (this.sessionStore.size > 0) {
|
|
5008
5137
|
this._status.set('connected');
|
|
5009
|
-
logger$
|
|
5138
|
+
logger$a.info('Restored sessions', { count: this.sessionStore.size });
|
|
5010
5139
|
}
|
|
5011
5140
|
}
|
|
5012
5141
|
catch (error) {
|
|
5013
|
-
logger$
|
|
5142
|
+
logger$a.error('Failed to restore sessions', {
|
|
5014
5143
|
error: error instanceof Error ? error.message : String(error),
|
|
5015
5144
|
});
|
|
5016
5145
|
}
|
|
@@ -5025,23 +5154,23 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5025
5154
|
* @param session - WalletConnect session object from IndexedDB
|
|
5026
5155
|
*/
|
|
5027
5156
|
async restoreSingleSession(session) {
|
|
5028
|
-
logger$
|
|
5029
|
-
logger$
|
|
5157
|
+
logger$a.debug('Restoring session', { topic: session.topic });
|
|
5158
|
+
logger$a.debug('Session namespaces', { namespaces: Object.keys(session.namespaces) });
|
|
5030
5159
|
// Determine ledger from session namespaces
|
|
5031
5160
|
let ledgerId;
|
|
5032
5161
|
let walletReportedNetwork;
|
|
5033
5162
|
if (session.namespaces.hedera) {
|
|
5034
5163
|
ledgerId = 'hedera';
|
|
5035
5164
|
walletReportedNetwork = session.namespaces.hedera.chains?.[0] || 'hedera:testnet';
|
|
5036
|
-
logger$
|
|
5165
|
+
logger$a.debug('Found Hedera namespace', { chains: session.namespaces.hedera.chains });
|
|
5037
5166
|
}
|
|
5038
5167
|
else if (session.namespaces.xrpl) {
|
|
5039
5168
|
ledgerId = 'xrpl';
|
|
5040
5169
|
walletReportedNetwork = session.namespaces.xrpl.chains?.[0] || 'xrpl:testnet';
|
|
5041
|
-
logger$
|
|
5170
|
+
logger$a.debug('Found XRPL namespace', { chains: session.namespaces.xrpl.chains });
|
|
5042
5171
|
}
|
|
5043
5172
|
if (!ledgerId || !walletReportedNetwork) {
|
|
5044
|
-
logger$
|
|
5173
|
+
logger$a.warn('Could not determine ledger for session', {
|
|
5045
5174
|
topic: session.topic,
|
|
5046
5175
|
availableNamespaces: Object.keys(session.namespaces),
|
|
5047
5176
|
});
|
|
@@ -5052,14 +5181,14 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5052
5181
|
const userSelectedNetwork = this.getUserNetworkSelection(session.topic);
|
|
5053
5182
|
const networkId = userSelectedNetwork || walletReportedNetwork;
|
|
5054
5183
|
if (userSelectedNetwork) {
|
|
5055
|
-
logger$
|
|
5184
|
+
logger$a.debug('Restored user network selection', {
|
|
5056
5185
|
userSelectedNetwork,
|
|
5057
5186
|
walletReportedNetwork,
|
|
5058
5187
|
topic: session.topic.substring(0, 16),
|
|
5059
5188
|
});
|
|
5060
5189
|
}
|
|
5061
5190
|
else {
|
|
5062
|
-
logger$
|
|
5191
|
+
logger$a.warn('No stored network selection found, using wallet-reported network', {
|
|
5063
5192
|
walletReportedNetwork,
|
|
5064
5193
|
topic: session.topic.substring(0, 16),
|
|
5065
5194
|
});
|
|
@@ -5070,18 +5199,18 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5070
5199
|
const sessionKey = this.sessionStore.generateSessionKey(ledgerId, networkId, session.topic);
|
|
5071
5200
|
// Parse accounts from session, filtering by user's selected network
|
|
5072
5201
|
const namespace = session.namespaces[ledgerId];
|
|
5073
|
-
logger$
|
|
5202
|
+
logger$a.debug('Namespace for restoration', {
|
|
5074
5203
|
ledgerId,
|
|
5075
5204
|
accountCount: namespace?.accounts?.length || 0,
|
|
5076
5205
|
});
|
|
5077
5206
|
// Pass the user's selected network to filter accounts appropriately
|
|
5078
5207
|
const parsedAccounts = signer.parseAccounts(namespace, userSelectedNetwork);
|
|
5079
|
-
logger$
|
|
5208
|
+
logger$a.debug('Parsed accounts from restored session', {
|
|
5080
5209
|
count: parsedAccounts.length,
|
|
5081
5210
|
userSelectedNetwork,
|
|
5082
5211
|
});
|
|
5083
5212
|
if (parsedAccounts.length === 0) {
|
|
5084
|
-
logger$
|
|
5213
|
+
logger$a.warn('Restored session has no accounts for selected network', {
|
|
5085
5214
|
userSelectedNetwork,
|
|
5086
5215
|
walletReportedNetwork,
|
|
5087
5216
|
});
|
|
@@ -5113,7 +5242,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5113
5242
|
signer,
|
|
5114
5243
|
accounts,
|
|
5115
5244
|
});
|
|
5116
|
-
logger$
|
|
5245
|
+
logger$a.info('Restored session', {
|
|
5117
5246
|
sessionKey,
|
|
5118
5247
|
ledgerId,
|
|
5119
5248
|
networkId,
|
|
@@ -5140,13 +5269,13 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5140
5269
|
storeUserNetworkSelection(sessionTopic, networkId) {
|
|
5141
5270
|
try {
|
|
5142
5271
|
localStorage.setItem(`${NETWORK_SELECTION_STORAGE_KEY}${sessionTopic}`, networkId);
|
|
5143
|
-
logger$
|
|
5272
|
+
logger$a.debug('Stored user network selection', {
|
|
5144
5273
|
topic: sessionTopic.substring(0, 16),
|
|
5145
5274
|
networkId,
|
|
5146
5275
|
});
|
|
5147
5276
|
}
|
|
5148
5277
|
catch (e) {
|
|
5149
|
-
logger$
|
|
5278
|
+
logger$a.warn('Failed to store network selection', { error: e.message });
|
|
5150
5279
|
}
|
|
5151
5280
|
}
|
|
5152
5281
|
/**
|
|
@@ -5160,7 +5289,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5160
5289
|
return localStorage.getItem(`${NETWORK_SELECTION_STORAGE_KEY}${sessionTopic}`) || undefined;
|
|
5161
5290
|
}
|
|
5162
5291
|
catch (e) {
|
|
5163
|
-
logger$
|
|
5292
|
+
logger$a.warn('Failed to retrieve network selection', { error: e.message });
|
|
5164
5293
|
return undefined;
|
|
5165
5294
|
}
|
|
5166
5295
|
}
|
|
@@ -5206,7 +5335,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5206
5335
|
}
|
|
5207
5336
|
// Get the appropriate signer for this ledger (Strategy Pattern)
|
|
5208
5337
|
const signer = SignerFactory.getSigner(wcConfig.ledgerId);
|
|
5209
|
-
logger$
|
|
5338
|
+
logger$a.debug('Using signer for ledger', { ledgerId: wcConfig.ledgerId });
|
|
5210
5339
|
// Initialize client via manager
|
|
5211
5340
|
await this.clientManager.initialize(projectId, {
|
|
5212
5341
|
name: wcConfig.appName || 'HSuite Demo',
|
|
@@ -5220,14 +5349,14 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5220
5349
|
// Build namespace configuration using signer
|
|
5221
5350
|
const namespaceConfig = signer.buildNamespace(wcConfig.networkId);
|
|
5222
5351
|
const optionalNamespaces = { [wcConfig.ledgerId]: namespaceConfig };
|
|
5223
|
-
logger$
|
|
5352
|
+
logger$a.debug('Namespace config', { ledgerId: wcConfig.ledgerId });
|
|
5224
5353
|
// Build session properties to pass user's preferred network to wallet
|
|
5225
5354
|
// This tells the wallet which network accounts to show in the picker
|
|
5226
5355
|
const sessionProperties = {
|
|
5227
5356
|
preferredNetwork: wcConfig.networkId,
|
|
5228
5357
|
preferredLedger: wcConfig.ledgerId,
|
|
5229
5358
|
};
|
|
5230
|
-
logger$
|
|
5359
|
+
logger$a.info('Connecting with preferred network', {
|
|
5231
5360
|
preferredNetwork: wcConfig.networkId,
|
|
5232
5361
|
preferredLedger: wcConfig.ledgerId,
|
|
5233
5362
|
});
|
|
@@ -5237,10 +5366,10 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5237
5366
|
await modal.openModal({ uri });
|
|
5238
5367
|
}
|
|
5239
5368
|
// Wait for wallet approval
|
|
5240
|
-
logger$
|
|
5369
|
+
logger$a.info('Waiting for wallet approval...');
|
|
5241
5370
|
const session = await approval();
|
|
5242
5371
|
this.clientManager.closeModal();
|
|
5243
|
-
logger$
|
|
5372
|
+
logger$a.info('[WC:DAPP:APPROVED] Session approved by wallet', {
|
|
5244
5373
|
topic: session.topic.substring(0, 16) + '...',
|
|
5245
5374
|
fullTopic: session.topic,
|
|
5246
5375
|
namespaces: Object.keys(session.namespaces),
|
|
@@ -5250,12 +5379,12 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5250
5379
|
const sessionKey = this.sessionStore.generateSessionKey(wcConfig.ledgerId, wcConfig.networkId, session.topic);
|
|
5251
5380
|
// Parse accounts from session
|
|
5252
5381
|
const sessionNamespace = session.namespaces[wcConfig.ledgerId];
|
|
5253
|
-
logger$
|
|
5382
|
+
logger$a.debug('Session namespace for ledger', {
|
|
5254
5383
|
ledgerId: wcConfig.ledgerId,
|
|
5255
5384
|
accountCount: sessionNamespace?.accounts?.length || 0,
|
|
5256
5385
|
});
|
|
5257
5386
|
if (!sessionNamespace) {
|
|
5258
|
-
logger$
|
|
5387
|
+
logger$a.error('No namespace found for ledger', {
|
|
5259
5388
|
ledgerId: wcConfig.ledgerId,
|
|
5260
5389
|
availableNamespaces: Object.keys(session.namespaces),
|
|
5261
5390
|
});
|
|
@@ -5264,12 +5393,12 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5264
5393
|
// Parse accounts from session namespace, filtering by user's selected network
|
|
5265
5394
|
// This ensures we only show accounts that match the network the user selected
|
|
5266
5395
|
const parsedAccounts = signer.parseAccounts(sessionNamespace, wcConfig.networkId);
|
|
5267
|
-
logger$
|
|
5396
|
+
logger$a.debug('Parsed accounts', {
|
|
5268
5397
|
count: parsedAccounts.length,
|
|
5269
5398
|
userSelectedNetwork: wcConfig.networkId,
|
|
5270
5399
|
});
|
|
5271
5400
|
if (parsedAccounts.length === 0) {
|
|
5272
|
-
logger$
|
|
5401
|
+
logger$a.warn('No accounts returned from wallet - possible causes:', {
|
|
5273
5402
|
reason1: 'Wallet has no accounts for the requested network',
|
|
5274
5403
|
reason2: 'Wallet denied account sharing',
|
|
5275
5404
|
reason3: 'Wallet returned accounts in unexpected format',
|
|
@@ -5308,7 +5437,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5308
5437
|
this.storeUserNetworkSelection(session.topic, wcConfig.networkId);
|
|
5309
5438
|
// Verify session exists in WC SDK after storing
|
|
5310
5439
|
const sdkSession = this.clientManager.getSession(session.topic);
|
|
5311
|
-
logger$
|
|
5440
|
+
logger$a.info('[WC:DAPP:STORED] Session stored locally', {
|
|
5312
5441
|
sessionKey,
|
|
5313
5442
|
topic: session.topic.substring(0, 16) + '...',
|
|
5314
5443
|
fullTopic: session.topic,
|
|
@@ -5337,7 +5466,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5337
5466
|
catch (error) {
|
|
5338
5467
|
this._status.set('error');
|
|
5339
5468
|
this._error.set(error instanceof Error ? error.message : 'Connection failed');
|
|
5340
|
-
logger$
|
|
5469
|
+
logger$a.error('Connection failed', {
|
|
5341
5470
|
error: error instanceof Error ? error.message : String(error),
|
|
5342
5471
|
});
|
|
5343
5472
|
// Emit provider error event
|
|
@@ -5402,14 +5531,14 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5402
5531
|
// WalletConnect events arrive outside Angular's zone
|
|
5403
5532
|
this.clientManager.on('session_delete', (event) => {
|
|
5404
5533
|
this.zone.run(() => {
|
|
5405
|
-
logger$
|
|
5534
|
+
logger$a.info('� Session deleted by wallet (session_delete event)', {
|
|
5406
5535
|
topic: event.topic,
|
|
5407
5536
|
});
|
|
5408
5537
|
this.removeSessionByTopic(event.topic);
|
|
5409
5538
|
});
|
|
5410
5539
|
});
|
|
5411
5540
|
this.sessionDeleteListenerRegistered = true;
|
|
5412
|
-
logger$
|
|
5541
|
+
logger$a.debug('session_delete listener registered');
|
|
5413
5542
|
}
|
|
5414
5543
|
/**
|
|
5415
5544
|
* Disconnect from WalletConnect sessions.
|
|
@@ -5419,7 +5548,7 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5419
5548
|
*/
|
|
5420
5549
|
async disconnect(sessionKey) {
|
|
5421
5550
|
if (!this.clientManager.isInitialized()) {
|
|
5422
|
-
logger$
|
|
5551
|
+
logger$a.warn('No client to disconnect');
|
|
5423
5552
|
return;
|
|
5424
5553
|
}
|
|
5425
5554
|
if (sessionKey) {
|
|
@@ -5431,10 +5560,10 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5431
5560
|
code: 6000,
|
|
5432
5561
|
message: 'User disconnected session',
|
|
5433
5562
|
});
|
|
5434
|
-
logger$
|
|
5563
|
+
logger$a.info('Disconnected session', { sessionKey });
|
|
5435
5564
|
}
|
|
5436
5565
|
catch (e) {
|
|
5437
|
-
logger$
|
|
5566
|
+
logger$a.error('Disconnect error for session', {
|
|
5438
5567
|
sessionKey,
|
|
5439
5568
|
error: e instanceof Error ? e.message : String(e),
|
|
5440
5569
|
});
|
|
@@ -5459,14 +5588,14 @@ class WalletConnectV2Provider extends BaseWalletProvider {
|
|
|
5459
5588
|
code: 6000,
|
|
5460
5589
|
message: 'User disconnected all sessions',
|
|
5461
5590
|
})
|
|
5462
|
-
.catch((err) => logger$
|
|
5591
|
+
.catch((err) => logger$a.error('Disconnect error', {
|
|
5463
5592
|
error: err.message,
|
|
5464
5593
|
})));
|
|
5465
5594
|
}
|
|
5466
5595
|
await Promise.all(disconnectPromises);
|
|
5467
5596
|
this.sessionStore.clear();
|
|
5468
5597
|
this._status.set('disconnected');
|
|
5469
|
-
logger$
|
|
5598
|
+
logger$a.info('Disconnected all sessions');
|
|
5470
5599
|
}
|
|
5471
5600
|
this.clientManager.closeModal();
|
|
5472
5601
|
}
|
|
@@ -5569,7 +5698,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
5569
5698
|
* - RPC communication over best available transport
|
|
5570
5699
|
* - Session ownership remains with the wallet
|
|
5571
5700
|
*/
|
|
5572
|
-
const logger$
|
|
5701
|
+
const logger$9 = getLogger().scoped?.('P2PSessionManager') ?? getLogger();
|
|
5573
5702
|
/**
|
|
5574
5703
|
* Mobile Native Session Manager
|
|
5575
5704
|
*
|
|
@@ -5638,7 +5767,7 @@ class P2PSessionManager {
|
|
|
5638
5767
|
* @returns Promise resolving to offer result with QR data
|
|
5639
5768
|
*/
|
|
5640
5769
|
async createPairingOffer(options) {
|
|
5641
|
-
logger$
|
|
5770
|
+
logger$9.info('Creating pairing offer (Nostr-first)', { options });
|
|
5642
5771
|
try {
|
|
5643
5772
|
this.runInZone(() => this._connectionState.set('creating_offer'));
|
|
5644
5773
|
// Clean up any existing client
|
|
@@ -5651,12 +5780,12 @@ class P2PSessionManager {
|
|
|
5651
5780
|
this.client = new ChannelClient({
|
|
5652
5781
|
p2p: { enabled: true },
|
|
5653
5782
|
onStateChange: (state) => {
|
|
5654
|
-
logger$
|
|
5783
|
+
logger$9.debug('ChannelClient state callback', { state });
|
|
5655
5784
|
const accounts = this.client?.accounts ?? [];
|
|
5656
5785
|
this.handleStateChange(state, accounts);
|
|
5657
5786
|
},
|
|
5658
5787
|
onAccountsChange: (accounts) => {
|
|
5659
|
-
logger$
|
|
5788
|
+
logger$9.debug('ChannelClient accounts callback', { count: accounts.length });
|
|
5660
5789
|
const state = this.client?.state ?? 'idle';
|
|
5661
5790
|
this.handleStateChange(state, accounts);
|
|
5662
5791
|
},
|
|
@@ -5681,7 +5810,7 @@ class P2PSessionManager {
|
|
|
5681
5810
|
// Encode invite for QR code
|
|
5682
5811
|
const qrData = encodeChannelInvite(invite);
|
|
5683
5812
|
this.runInZone(() => this._connectionState.set('awaiting_connection'));
|
|
5684
|
-
logger$
|
|
5813
|
+
logger$9.info('Pairing offer created', {
|
|
5685
5814
|
sessionId: invite.id.slice(0, 8),
|
|
5686
5815
|
qrDataLength: qrData.length,
|
|
5687
5816
|
});
|
|
@@ -5690,7 +5819,7 @@ class P2PSessionManager {
|
|
|
5690
5819
|
return { qrData, sessionId: invite.id };
|
|
5691
5820
|
}
|
|
5692
5821
|
catch (error) {
|
|
5693
|
-
logger$
|
|
5822
|
+
logger$9.error('Failed to create pairing offer', { error });
|
|
5694
5823
|
this.runInZone(() => this._connectionState.set('error'));
|
|
5695
5824
|
this.notifyError(error instanceof Error ? error : new Error('Failed to create offer'));
|
|
5696
5825
|
throw error;
|
|
@@ -5708,7 +5837,7 @@ class P2PSessionManager {
|
|
|
5708
5837
|
if (!this.client || !this.currentInvite) {
|
|
5709
5838
|
throw new Error('No active pairing offer - call createPairingOffer first');
|
|
5710
5839
|
}
|
|
5711
|
-
logger$
|
|
5840
|
+
logger$9.info('Waiting for wallet to connect and approve session...');
|
|
5712
5841
|
return new Promise((resolve, reject) => {
|
|
5713
5842
|
this.sessionApprovalPromise = { resolve, reject };
|
|
5714
5843
|
});
|
|
@@ -5719,7 +5848,7 @@ class P2PSessionManager {
|
|
|
5719
5848
|
* Cleans up the transport without fully disconnecting.
|
|
5720
5849
|
*/
|
|
5721
5850
|
async cancelPairingOffer() {
|
|
5722
|
-
logger$
|
|
5851
|
+
logger$9.debug('Cancelling pairing offer');
|
|
5723
5852
|
if (this.sessionApprovalPromise) {
|
|
5724
5853
|
this.sessionApprovalPromise.reject(new Error('Pairing cancelled'));
|
|
5725
5854
|
this.sessionApprovalPromise = undefined;
|
|
@@ -5761,7 +5890,7 @@ class P2PSessionManager {
|
|
|
5761
5890
|
*/
|
|
5762
5891
|
async signTransaction(options) {
|
|
5763
5892
|
this.requireSession();
|
|
5764
|
-
logger$
|
|
5893
|
+
logger$9.info('Signing transaction', { account: options.accountAddress });
|
|
5765
5894
|
if (!this.client) {
|
|
5766
5895
|
throw new Error('Not connected');
|
|
5767
5896
|
}
|
|
@@ -5783,7 +5912,7 @@ class P2PSessionManager {
|
|
|
5783
5912
|
*/
|
|
5784
5913
|
async submitTransaction(options) {
|
|
5785
5914
|
this.requireSession();
|
|
5786
|
-
logger$
|
|
5915
|
+
logger$9.info('Submitting transaction', { account: options.accountAddress });
|
|
5787
5916
|
if (!this.client) {
|
|
5788
5917
|
throw new Error('Not connected');
|
|
5789
5918
|
}
|
|
@@ -5808,7 +5937,7 @@ class P2PSessionManager {
|
|
|
5808
5937
|
*/
|
|
5809
5938
|
async signMessage(options) {
|
|
5810
5939
|
this.requireSession();
|
|
5811
|
-
logger$
|
|
5940
|
+
logger$9.info('Signing message', { account: options.accountAddress });
|
|
5812
5941
|
if (!this.client) {
|
|
5813
5942
|
throw new Error('Not connected');
|
|
5814
5943
|
}
|
|
@@ -5833,7 +5962,7 @@ class P2PSessionManager {
|
|
|
5833
5962
|
* Terminate the current session
|
|
5834
5963
|
*/
|
|
5835
5964
|
async terminateSession() {
|
|
5836
|
-
logger$
|
|
5965
|
+
logger$9.info('Terminating session');
|
|
5837
5966
|
if (this.client) {
|
|
5838
5967
|
await this.client.disconnect();
|
|
5839
5968
|
}
|
|
@@ -5934,6 +6063,7 @@ class P2PSessionManager {
|
|
|
5934
6063
|
accounts: accounts.map((acc, index) => ({
|
|
5935
6064
|
id: `p2p-${acc.address}`,
|
|
5936
6065
|
address: acc.address,
|
|
6066
|
+
publicKey: acc.publicKey,
|
|
5937
6067
|
label: acc.alias || `Account ${index + 1}`,
|
|
5938
6068
|
ledgerId: acc.ledgerId,
|
|
5939
6069
|
networkId: acc.networkId,
|
|
@@ -5952,7 +6082,7 @@ class P2PSessionManager {
|
|
|
5952
6082
|
* @param reason
|
|
5953
6083
|
*/
|
|
5954
6084
|
handleDisconnection(reason) {
|
|
5955
|
-
logger$
|
|
6085
|
+
logger$9.info('Handling disconnection', { reason });
|
|
5956
6086
|
this.cleanup();
|
|
5957
6087
|
this.runInZone(() => {
|
|
5958
6088
|
this._currentSession.set(null);
|
|
@@ -6004,7 +6134,7 @@ class P2PSessionManager {
|
|
|
6004
6134
|
callback(session);
|
|
6005
6135
|
}
|
|
6006
6136
|
catch (error) {
|
|
6007
|
-
logger$
|
|
6137
|
+
logger$9.error('Connected callback error', { error });
|
|
6008
6138
|
}
|
|
6009
6139
|
}
|
|
6010
6140
|
}
|
|
@@ -6017,7 +6147,7 @@ class P2PSessionManager {
|
|
|
6017
6147
|
callback();
|
|
6018
6148
|
}
|
|
6019
6149
|
catch (error) {
|
|
6020
|
-
logger$
|
|
6150
|
+
logger$9.error('Disconnected callback error', { error });
|
|
6021
6151
|
}
|
|
6022
6152
|
}
|
|
6023
6153
|
}
|
|
@@ -6031,7 +6161,7 @@ class P2PSessionManager {
|
|
|
6031
6161
|
callback(error);
|
|
6032
6162
|
}
|
|
6033
6163
|
catch (err) {
|
|
6034
|
-
logger$
|
|
6164
|
+
logger$9.error('Error callback error', { err });
|
|
6035
6165
|
}
|
|
6036
6166
|
}
|
|
6037
6167
|
}
|
|
@@ -6073,7 +6203,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
6073
6203
|
* 4. User approves session in wallet
|
|
6074
6204
|
* 5. P2P upgrade happens automatically in background
|
|
6075
6205
|
*/
|
|
6076
|
-
const logger$
|
|
6206
|
+
const logger$8 = getLogger().scoped?.('P2PNativeProvider') ?? getLogger();
|
|
6077
6207
|
/**
|
|
6078
6208
|
* Storage key for mobile session metadata (separate from hsuite-native)
|
|
6079
6209
|
*/
|
|
@@ -6098,10 +6228,10 @@ class P2PSessionPersistence {
|
|
|
6098
6228
|
timestamp: Date.now(),
|
|
6099
6229
|
};
|
|
6100
6230
|
window.localStorage.setItem(P2P_SESSION_STORAGE_KEY, JSON.stringify(payload));
|
|
6101
|
-
logger$
|
|
6231
|
+
logger$8.debug('Mobile session persisted', { sessionId: session.sessionId });
|
|
6102
6232
|
}
|
|
6103
6233
|
catch (error) {
|
|
6104
|
-
logger$
|
|
6234
|
+
logger$8.warn('Failed to persist mobile session', { error });
|
|
6105
6235
|
}
|
|
6106
6236
|
}
|
|
6107
6237
|
/**
|
|
@@ -6119,11 +6249,11 @@ class P2PSessionPersistence {
|
|
|
6119
6249
|
this.clear();
|
|
6120
6250
|
return null;
|
|
6121
6251
|
}
|
|
6122
|
-
logger$
|
|
6252
|
+
logger$8.info('Restored mobile session metadata', { sessionId: stored.sessionId });
|
|
6123
6253
|
return stored;
|
|
6124
6254
|
}
|
|
6125
6255
|
catch (error) {
|
|
6126
|
-
logger$
|
|
6256
|
+
logger$8.warn('Failed to restore mobile session', { error });
|
|
6127
6257
|
return null;
|
|
6128
6258
|
}
|
|
6129
6259
|
}
|
|
@@ -6135,10 +6265,10 @@ class P2PSessionPersistence {
|
|
|
6135
6265
|
if (typeof window === 'undefined')
|
|
6136
6266
|
return;
|
|
6137
6267
|
window.localStorage.removeItem(P2P_SESSION_STORAGE_KEY);
|
|
6138
|
-
logger$
|
|
6268
|
+
logger$8.debug('Mobile session cleared');
|
|
6139
6269
|
}
|
|
6140
6270
|
catch (error) {
|
|
6141
|
-
logger$
|
|
6271
|
+
logger$8.warn('Failed to clear mobile session', { error });
|
|
6142
6272
|
}
|
|
6143
6273
|
}
|
|
6144
6274
|
/**
|
|
@@ -6258,7 +6388,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6258
6388
|
restorePersistedSession() {
|
|
6259
6389
|
const stored = this.sessionPersistence.restore();
|
|
6260
6390
|
if (stored) {
|
|
6261
|
-
logger$
|
|
6391
|
+
logger$8.info('Restoring persisted mobile session', {
|
|
6262
6392
|
sessionId: stored.sessionId,
|
|
6263
6393
|
accountCount: stored.accounts?.length ?? 0,
|
|
6264
6394
|
});
|
|
@@ -6269,7 +6399,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6269
6399
|
}
|
|
6270
6400
|
// Note: We don't set connected status because WebRTC needs re-pairing
|
|
6271
6401
|
// The persisted data is for account info only
|
|
6272
|
-
logger$
|
|
6402
|
+
logger$8.debug('Mobile session metadata restored (requires re-pairing for connection)');
|
|
6273
6403
|
}
|
|
6274
6404
|
}
|
|
6275
6405
|
/**
|
|
@@ -6292,7 +6422,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6292
6422
|
* @throws {Error} If offer generation fails
|
|
6293
6423
|
*/
|
|
6294
6424
|
async createPairingOffer(config) {
|
|
6295
|
-
logger$
|
|
6425
|
+
logger$8.info('Creating pairing offer', { config });
|
|
6296
6426
|
try {
|
|
6297
6427
|
this._connectionState.set('generating_offer');
|
|
6298
6428
|
this._error.set(null);
|
|
@@ -6315,14 +6445,14 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6315
6445
|
this._pairingOffer.set(offerResult);
|
|
6316
6446
|
this._connectionState.set('awaiting_scan');
|
|
6317
6447
|
this._status.set('connecting');
|
|
6318
|
-
logger$
|
|
6448
|
+
logger$8.info('Pairing offer created', {
|
|
6319
6449
|
qrDataLength: result.qrData.length,
|
|
6320
6450
|
expiresAt: offerResult.expiresAt,
|
|
6321
6451
|
});
|
|
6322
6452
|
return offerResult;
|
|
6323
6453
|
}
|
|
6324
6454
|
catch (error) {
|
|
6325
|
-
logger$
|
|
6455
|
+
logger$8.error('Failed to create pairing offer', { error });
|
|
6326
6456
|
this._connectionState.set('error');
|
|
6327
6457
|
this._status.set('error');
|
|
6328
6458
|
this._error.set(error instanceof Error ? error.message : 'Failed to create pairing offer');
|
|
@@ -6336,7 +6466,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6336
6466
|
* @returns Promise resolving to new pairing offer
|
|
6337
6467
|
*/
|
|
6338
6468
|
async refreshPairingOffer(config) {
|
|
6339
|
-
logger$
|
|
6469
|
+
logger$8.debug('Refreshing pairing offer');
|
|
6340
6470
|
// Cancel any existing offer
|
|
6341
6471
|
await this.sessionManager.cancelPairingOffer();
|
|
6342
6472
|
return this.createPairingOffer(config);
|
|
@@ -6372,7 +6502,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6372
6502
|
* @throws {Error} If no pairing offer was created
|
|
6373
6503
|
*/
|
|
6374
6504
|
async waitForSessionApproval() {
|
|
6375
|
-
logger$
|
|
6505
|
+
logger$8.info('Waiting for wallet to connect and approve session');
|
|
6376
6506
|
this._connectionState.set('pending_approval');
|
|
6377
6507
|
try {
|
|
6378
6508
|
// This blocks until wallet connects via Nostr and user approves
|
|
@@ -6390,7 +6520,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6390
6520
|
return session;
|
|
6391
6521
|
}
|
|
6392
6522
|
catch (error) {
|
|
6393
|
-
logger$
|
|
6523
|
+
logger$8.error('Session approval failed', { error });
|
|
6394
6524
|
this._connectionState.set('error');
|
|
6395
6525
|
throw error;
|
|
6396
6526
|
}
|
|
@@ -6415,10 +6545,10 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6415
6545
|
* @throws {Error} If connection times out (2 minutes)
|
|
6416
6546
|
*/
|
|
6417
6547
|
async connect(config) {
|
|
6418
|
-
logger$
|
|
6548
|
+
logger$8.info('Connect called', { config });
|
|
6419
6549
|
// If already connected, no-op
|
|
6420
6550
|
if (this._status() === 'connected') {
|
|
6421
|
-
logger$
|
|
6551
|
+
logger$8.debug('Already connected');
|
|
6422
6552
|
return;
|
|
6423
6553
|
}
|
|
6424
6554
|
// Create pairing offer and wait for connection
|
|
@@ -6446,7 +6576,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6446
6576
|
* persisted session data. The wallet is notified of the disconnection.
|
|
6447
6577
|
*/
|
|
6448
6578
|
async disconnect() {
|
|
6449
|
-
logger$
|
|
6579
|
+
logger$8.info('Disconnecting');
|
|
6450
6580
|
try {
|
|
6451
6581
|
await this.sessionManager.terminateSession();
|
|
6452
6582
|
}
|
|
@@ -6478,7 +6608,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6478
6608
|
if (this._status() !== 'connected') {
|
|
6479
6609
|
throw new Error('Not connected to wallet');
|
|
6480
6610
|
}
|
|
6481
|
-
logger$
|
|
6611
|
+
logger$8.info('Signing transaction', { account: options.accountAddress });
|
|
6482
6612
|
const result = await this.sessionManager.signTransaction({
|
|
6483
6613
|
accountAddress: options.accountAddress,
|
|
6484
6614
|
payload: options.payload,
|
|
@@ -6509,7 +6639,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6509
6639
|
if (this._status() !== 'connected') {
|
|
6510
6640
|
throw new Error('Not connected to wallet');
|
|
6511
6641
|
}
|
|
6512
|
-
logger$
|
|
6642
|
+
logger$8.info('Submitting transaction', { account: options.accountAddress });
|
|
6513
6643
|
const result = await this.sessionManager.submitTransaction({
|
|
6514
6644
|
accountAddress: options.accountAddress,
|
|
6515
6645
|
payload: options.payload,
|
|
@@ -6561,7 +6691,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6561
6691
|
if (this._status() !== 'connected') {
|
|
6562
6692
|
throw new Error('Not connected to wallet');
|
|
6563
6693
|
}
|
|
6564
|
-
logger$
|
|
6694
|
+
logger$8.info('Signing message', { account: options.accountAddress });
|
|
6565
6695
|
const result = await this.sessionManager.signMessage({
|
|
6566
6696
|
accountAddress: options.accountAddress,
|
|
6567
6697
|
message: options.message,
|
|
@@ -6596,7 +6726,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6596
6726
|
// Connection established — wrap in NgZone to ensure Angular change
|
|
6597
6727
|
// detection picks up signal updates from Nostr/WebSocket callbacks
|
|
6598
6728
|
this.sessionManager.onConnected((session) => {
|
|
6599
|
-
logger$
|
|
6729
|
+
logger$8.info('Session connected', { sessionId: session?.id });
|
|
6600
6730
|
this.ngZone.run(() => {
|
|
6601
6731
|
this._connectionState.set('connected');
|
|
6602
6732
|
this._status.set('connected');
|
|
@@ -6614,7 +6744,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6614
6744
|
});
|
|
6615
6745
|
// Session terminated
|
|
6616
6746
|
this.sessionManager.onDisconnected(() => {
|
|
6617
|
-
logger$
|
|
6747
|
+
logger$8.info('Session disconnected');
|
|
6618
6748
|
this.ngZone.run(() => {
|
|
6619
6749
|
this._connectionState.set('disconnected');
|
|
6620
6750
|
this._status.set('disconnected');
|
|
@@ -6625,7 +6755,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6625
6755
|
});
|
|
6626
6756
|
// Error occurred
|
|
6627
6757
|
this.sessionManager.onError((error) => {
|
|
6628
|
-
logger$
|
|
6758
|
+
logger$8.error('Session error', { error });
|
|
6629
6759
|
this.ngZone.run(() => {
|
|
6630
6760
|
this._connectionState.set('error');
|
|
6631
6761
|
this._status.set('error');
|
|
@@ -6634,7 +6764,7 @@ class P2PNativeProvider extends BaseWalletProvider {
|
|
|
6634
6764
|
});
|
|
6635
6765
|
// Accounts updated
|
|
6636
6766
|
this.sessionManager.onAccountsChanged((accounts) => {
|
|
6637
|
-
logger$
|
|
6767
|
+
logger$8.debug('Accounts updated', { count: accounts.length });
|
|
6638
6768
|
this.ngZone.run(() => {
|
|
6639
6769
|
const accountsWithProvider = this.ensureProviderIdOnAccounts(accounts);
|
|
6640
6770
|
this._accounts.set(accountsWithProvider);
|
|
@@ -6704,7 +6834,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
6704
6834
|
* +-------------------------------------------------------------------+
|
|
6705
6835
|
* ```
|
|
6706
6836
|
*/
|
|
6707
|
-
const logger$
|
|
6837
|
+
const logger$7 = getLogger().scoped?.('UnifiedWalletService') ?? getLogger();
|
|
6708
6838
|
/**
|
|
6709
6839
|
* Central service for managing wallet connections across multiple protocols.
|
|
6710
6840
|
*
|
|
@@ -6842,7 +6972,7 @@ class UnifiedWalletService {
|
|
|
6842
6972
|
return await reconnect.call(provider);
|
|
6843
6973
|
}
|
|
6844
6974
|
catch (error) {
|
|
6845
|
-
logger$
|
|
6975
|
+
logger$7.warn('Provider reconnect failed', { providerId, error });
|
|
6846
6976
|
return false;
|
|
6847
6977
|
}
|
|
6848
6978
|
}
|
|
@@ -6906,7 +7036,7 @@ class UnifiedWalletService {
|
|
|
6906
7036
|
*/
|
|
6907
7037
|
registerProvider(provider) {
|
|
6908
7038
|
if (this.providers.has(provider.id)) {
|
|
6909
|
-
logger$
|
|
7039
|
+
logger$7.warn('Provider already registered', { providerId: provider.id });
|
|
6910
7040
|
return;
|
|
6911
7041
|
}
|
|
6912
7042
|
this.providers.set(provider.id, provider);
|
|
@@ -7361,7 +7491,7 @@ class UnifiedWalletService {
|
|
|
7361
7491
|
switchAccount(accountId) {
|
|
7362
7492
|
const account = this.getAccountById(accountId);
|
|
7363
7493
|
if (!account) {
|
|
7364
|
-
logger$
|
|
7494
|
+
logger$7.warn('Account not found', { accountId });
|
|
7365
7495
|
return false;
|
|
7366
7496
|
}
|
|
7367
7497
|
this.setActiveAccount(account);
|
|
@@ -7416,7 +7546,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
7416
7546
|
* - Keyboard navigation support
|
|
7417
7547
|
* - Toast/alert integration
|
|
7418
7548
|
*/
|
|
7419
|
-
const logger$
|
|
7549
|
+
const logger$6 = getLogger().scoped?.('AccountSelectorService') ?? getLogger();
|
|
7420
7550
|
/**
|
|
7421
7551
|
* @service AccountSelectorService
|
|
7422
7552
|
* Business logic service for account selector component.
|
|
@@ -7505,7 +7635,7 @@ class AccountSelectorService {
|
|
|
7505
7635
|
return null;
|
|
7506
7636
|
}
|
|
7507
7637
|
catch (error) {
|
|
7508
|
-
logger$
|
|
7638
|
+
logger$6.error('Error getting currently connected account', { error });
|
|
7509
7639
|
return null;
|
|
7510
7640
|
}
|
|
7511
7641
|
}, ...(ngDevMode ? [{ debugName: "currentlyConnectedAccount" }] : []));
|
|
@@ -7533,7 +7663,7 @@ class AccountSelectorService {
|
|
|
7533
7663
|
return others;
|
|
7534
7664
|
}
|
|
7535
7665
|
catch (error) {
|
|
7536
|
-
logger$
|
|
7666
|
+
logger$6.error('Error getting other accounts', { error });
|
|
7537
7667
|
return [];
|
|
7538
7668
|
}
|
|
7539
7669
|
}, ...(ngDevMode ? [{ debugName: "otherAccounts" }] : []));
|
|
@@ -7562,10 +7692,10 @@ class AccountSelectorService {
|
|
|
7562
7692
|
// Modal mode: show toast and dismiss
|
|
7563
7693
|
if (mode === 'modal') {
|
|
7564
7694
|
await this.showSuccessToast(`Switched to ${account.label || 'Account'}`);
|
|
7565
|
-
logger$
|
|
7695
|
+
logger$6.info('[Modal] Account changed and modal dismissed', { ...accountData });
|
|
7566
7696
|
}
|
|
7567
7697
|
else {
|
|
7568
|
-
logger$
|
|
7698
|
+
logger$6.info('[Inline] Account changed', { ...accountData });
|
|
7569
7699
|
}
|
|
7570
7700
|
return accountData;
|
|
7571
7701
|
}
|
|
@@ -7617,7 +7747,7 @@ class AccountSelectorService {
|
|
|
7617
7747
|
*/
|
|
7618
7748
|
async disconnectAll(_mode) {
|
|
7619
7749
|
if (!this.alertController) {
|
|
7620
|
-
logger$
|
|
7750
|
+
logger$6.warn('AlertController not available for disconnect confirmation');
|
|
7621
7751
|
return false;
|
|
7622
7752
|
}
|
|
7623
7753
|
return new Promise((resolve) => {
|
|
@@ -7642,7 +7772,7 @@ class AccountSelectorService {
|
|
|
7642
7772
|
await this.wallet.disconnectSession(session.providerId, session.metadata?.sessionKey);
|
|
7643
7773
|
}
|
|
7644
7774
|
catch (error) {
|
|
7645
|
-
logger$
|
|
7775
|
+
logger$6.error('Failed to disconnect session', {
|
|
7646
7776
|
providerId: session.providerId,
|
|
7647
7777
|
error,
|
|
7648
7778
|
});
|
|
@@ -7652,7 +7782,7 @@ class AccountSelectorService {
|
|
|
7652
7782
|
resolve(true);
|
|
7653
7783
|
}
|
|
7654
7784
|
catch (error) {
|
|
7655
|
-
logger$
|
|
7785
|
+
logger$6.error('Failed to disconnect all sessions', { error });
|
|
7656
7786
|
this.error.set('Failed to disconnect all wallets');
|
|
7657
7787
|
resolve(false);
|
|
7658
7788
|
}
|
|
@@ -7737,11 +7867,11 @@ class AccountSelectorService {
|
|
|
7737
7867
|
if (mode === 'modal') {
|
|
7738
7868
|
await this.showSuccessToast('Wallet disconnected successfully');
|
|
7739
7869
|
}
|
|
7740
|
-
logger$
|
|
7870
|
+
logger$6.info('Session disconnected', { providerId, sessionKey, mode });
|
|
7741
7871
|
return true;
|
|
7742
7872
|
}
|
|
7743
7873
|
catch (error) {
|
|
7744
|
-
logger$
|
|
7874
|
+
logger$6.error('Failed to disconnect session', { providerId, error });
|
|
7745
7875
|
if (mode === 'modal') {
|
|
7746
7876
|
await this.showErrorToast('Failed to disconnect wallet. Please try again.');
|
|
7747
7877
|
}
|
|
@@ -8439,24 +8569,22 @@ class WalletAccountDisplayComponent {
|
|
|
8439
8569
|
}
|
|
8440
8570
|
return null;
|
|
8441
8571
|
}, ...(ngDevMode ? [{ debugName: "multisigInfo" }] : []));
|
|
8572
|
+
/**
|
|
8573
|
+
*
|
|
8574
|
+
*/
|
|
8442
8575
|
constructor() {
|
|
8443
8576
|
addIcons({ walletOutline, personOutline, peopleOutline });
|
|
8444
8577
|
}
|
|
8445
8578
|
/**
|
|
8446
8579
|
* Get color for ledger badge
|
|
8580
|
+
* @param ledgerId
|
|
8447
8581
|
*/
|
|
8448
8582
|
getLedgerColor(ledgerId) {
|
|
8449
|
-
|
|
8450
|
-
case 'hedera':
|
|
8451
|
-
return 'success';
|
|
8452
|
-
case 'xrpl':
|
|
8453
|
-
return 'tertiary';
|
|
8454
|
-
default:
|
|
8455
|
-
return 'primary';
|
|
8456
|
-
}
|
|
8583
|
+
return LedgerUIRegistry.get(ledgerId.toLowerCase())?.badgeColor ?? 'primary';
|
|
8457
8584
|
}
|
|
8458
8585
|
/**
|
|
8459
8586
|
* Format network ID for display
|
|
8587
|
+
* @param networkId
|
|
8460
8588
|
*/
|
|
8461
8589
|
formatNetwork(networkId) {
|
|
8462
8590
|
const parts = networkId.split(':');
|
|
@@ -9290,7 +9418,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
9290
9418
|
* - HSuite Native + Mobile QR: Network → Protocol → Method → QR Pairing (4 steps)
|
|
9291
9419
|
* - WalletConnect: Network → Protocol → Ledger → Connect (4 steps)
|
|
9292
9420
|
*/
|
|
9293
|
-
const logger$
|
|
9421
|
+
const logger$5 = getLogger().scoped?.('WalletConnectionModal') ?? getLogger();
|
|
9294
9422
|
/**
|
|
9295
9423
|
* Connection step enumeration for the wizard flow
|
|
9296
9424
|
*/
|
|
@@ -9303,6 +9431,18 @@ var ConnectionStep;
|
|
|
9303
9431
|
ConnectionStep[ConnectionStep["QrPairing"] = 5] = "QrPairing";
|
|
9304
9432
|
ConnectionStep[ConnectionStep["ConnectionProgress"] = 6] = "ConnectionProgress";
|
|
9305
9433
|
})(ConnectionStep || (ConnectionStep = {}));
|
|
9434
|
+
/**
|
|
9435
|
+
* Per-ledger copy for the WalletConnect "Choose Blockchain" step.
|
|
9436
|
+
*
|
|
9437
|
+
* Keyed by {@link SupportedLedger} (the closed set of ledgers the WalletConnect
|
|
9438
|
+
* provider brokers) via `satisfies`, so widening that union forces a description
|
|
9439
|
+
* here. Display name and icon come from {@link LedgerUIRegistry}; this map holds
|
|
9440
|
+
* only the connection-flow copy specific to this surface.
|
|
9441
|
+
*/
|
|
9442
|
+
const LEDGER_DESCRIPTIONS = {
|
|
9443
|
+
hedera: 'Hedera Hashgraph network',
|
|
9444
|
+
xrpl: 'XRP Ledger network',
|
|
9445
|
+
};
|
|
9306
9446
|
/**
|
|
9307
9447
|
* Main wallet connection modal component.
|
|
9308
9448
|
* Orchestrates the multi-step connection flow.
|
|
@@ -9314,6 +9454,15 @@ class WalletConnectionModalComponent {
|
|
|
9314
9454
|
* WalletConnect project ID (required for WalletConnect)
|
|
9315
9455
|
*/
|
|
9316
9456
|
projectId;
|
|
9457
|
+
/**
|
|
9458
|
+
* Pins the connection network and hides the network-selection step.
|
|
9459
|
+
*
|
|
9460
|
+
* When set to `'mainnet'` or `'testnet'`, the host dApp decides the network
|
|
9461
|
+
* and the user is never shown the network picker — the modal opens directly
|
|
9462
|
+
* on protocol selection. When omitted (default), the user chooses the network
|
|
9463
|
+
* as the first step.
|
|
9464
|
+
*/
|
|
9465
|
+
network;
|
|
9317
9466
|
/**
|
|
9318
9467
|
* App name for WalletConnect metadata
|
|
9319
9468
|
*/
|
|
@@ -9407,20 +9556,15 @@ class WalletConnectionModalComponent {
|
|
|
9407
9556
|
}
|
|
9408
9557
|
return options;
|
|
9409
9558
|
}, ...(ngDevMode ? [{ debugName: "protocolOptions" }] : []));
|
|
9410
|
-
ledgerOptions =
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
name: 'XRP Ledger',
|
|
9420
|
-
description: 'XRP Ledger network',
|
|
9421
|
-
icon: 'logo-usd', // Placeholder, use actual XRPL icon
|
|
9422
|
-
},
|
|
9423
|
-
];
|
|
9559
|
+
ledgerOptions = Object.keys(LEDGER_DESCRIPTIONS).map((id) => {
|
|
9560
|
+
const ui = LedgerUIRegistry.get(id);
|
|
9561
|
+
return {
|
|
9562
|
+
id,
|
|
9563
|
+
name: ui?.displayName ?? id,
|
|
9564
|
+
description: LEDGER_DESCRIPTIONS[id],
|
|
9565
|
+
icon: ui?.iconName ?? '',
|
|
9566
|
+
};
|
|
9567
|
+
});
|
|
9424
9568
|
/**
|
|
9425
9569
|
* Connection method options for HSuite Native.
|
|
9426
9570
|
* Simplified to two options:
|
|
@@ -9499,21 +9643,30 @@ class WalletConnectionModalComponent {
|
|
|
9499
9643
|
stepConfig = computed(() => {
|
|
9500
9644
|
const protocol = this.selectedProtocol();
|
|
9501
9645
|
const method = this.selectedConnectionMethod();
|
|
9646
|
+
let base;
|
|
9502
9647
|
// Initial state: show 3 placeholder steps until protocol is selected
|
|
9503
9648
|
if (!protocol) {
|
|
9504
|
-
|
|
9649
|
+
base = { total: 3, labels: ['Network', 'Protocol', '...'] };
|
|
9505
9650
|
}
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9651
|
+
else if (protocol === 'hsuite-native') {
|
|
9652
|
+
// HSuite Native path
|
|
9653
|
+
base =
|
|
9654
|
+
method === 'mobile-qr'
|
|
9655
|
+
? // Mobile QR needs 4 steps
|
|
9656
|
+
{ total: 4, labels: ['Network', 'Protocol', 'Method', 'Pair'] }
|
|
9657
|
+
: // Desktop connects immediately after method selection (3 steps)
|
|
9658
|
+
{ total: 3, labels: ['Network', 'Protocol', 'Method'] };
|
|
9659
|
+
}
|
|
9660
|
+
else {
|
|
9661
|
+
// WalletConnect path: 4 steps
|
|
9662
|
+
base = { total: 4, labels: ['Network', 'Protocol', 'Ledger', 'Connect'] };
|
|
9514
9663
|
}
|
|
9515
|
-
//
|
|
9516
|
-
|
|
9664
|
+
// When the network is pinned by the host dApp the network step is never
|
|
9665
|
+
// shown — drop it from the indicator so the numbering stays accurate.
|
|
9666
|
+
if (this.network) {
|
|
9667
|
+
return { total: base.total - 1, labels: base.labels.slice(1) };
|
|
9668
|
+
}
|
|
9669
|
+
return base;
|
|
9517
9670
|
}, ...(ngDevMode ? [{ debugName: "stepConfig" }] : []));
|
|
9518
9671
|
/**
|
|
9519
9672
|
* Maps the internal ConnectionStep enum to display position (1-based).
|
|
@@ -9523,20 +9676,23 @@ class WalletConnectionModalComponent {
|
|
|
9523
9676
|
const step = this.currentStep();
|
|
9524
9677
|
// Protocol could be used for protocol-specific step mapping in the future
|
|
9525
9678
|
const _protocol = this.selectedProtocol();
|
|
9679
|
+
// When the network is pinned the network step is removed, so every
|
|
9680
|
+
// subsequent step shifts down one position in the indicator.
|
|
9681
|
+
const networkOffset = this.network ? 1 : 0;
|
|
9526
9682
|
// Map internal step to display position based on protocol path
|
|
9527
9683
|
switch (step) {
|
|
9528
9684
|
case ConnectionStep.NetworkSelection:
|
|
9529
9685
|
return 1;
|
|
9530
9686
|
case ConnectionStep.ProtocolSelection:
|
|
9531
|
-
return 2;
|
|
9687
|
+
return 2 - networkOffset;
|
|
9532
9688
|
case ConnectionStep.ConnectionMethodSelection:
|
|
9533
|
-
return 3;
|
|
9689
|
+
return 3 - networkOffset;
|
|
9534
9690
|
case ConnectionStep.LedgerSelection:
|
|
9535
9691
|
// WalletConnect ledger selection is step 3
|
|
9536
|
-
return 3;
|
|
9692
|
+
return 3 - networkOffset;
|
|
9537
9693
|
case ConnectionStep.QrPairing:
|
|
9538
9694
|
// QR pairing is step 4 for both paths that use it
|
|
9539
|
-
return 4;
|
|
9695
|
+
return 4 - networkOffset;
|
|
9540
9696
|
case ConnectionStep.ConnectionProgress:
|
|
9541
9697
|
// Connection progress is always the last step
|
|
9542
9698
|
return this.stepConfig().total;
|
|
@@ -9544,6 +9700,22 @@ class WalletConnectionModalComponent {
|
|
|
9544
9700
|
return 1;
|
|
9545
9701
|
}
|
|
9546
9702
|
}, ...(ngDevMode ? [{ debugName: "displayStep" }] : []));
|
|
9703
|
+
/**
|
|
9704
|
+
* Whether a back affordance should be offered for the current step.
|
|
9705
|
+
* The first reachable step has no back target — that is protocol selection
|
|
9706
|
+
* when the network is pinned, otherwise network selection. The connection
|
|
9707
|
+
* progress step manages its own retry navigation instead.
|
|
9708
|
+
*/
|
|
9709
|
+
canGoBack = computed(() => {
|
|
9710
|
+
const step = this.currentStep();
|
|
9711
|
+
if (step === ConnectionStep.ConnectionProgress) {
|
|
9712
|
+
return false;
|
|
9713
|
+
}
|
|
9714
|
+
const firstStep = this.network
|
|
9715
|
+
? ConnectionStep.ProtocolSelection
|
|
9716
|
+
: ConnectionStep.NetworkSelection;
|
|
9717
|
+
return step > firstStep;
|
|
9718
|
+
}, ...(ngDevMode ? [{ debugName: "canGoBack" }] : []));
|
|
9547
9719
|
/**
|
|
9548
9720
|
*
|
|
9549
9721
|
* @param modalController
|
|
@@ -9556,6 +9728,21 @@ class WalletConnectionModalComponent {
|
|
|
9556
9728
|
this.p2pNativeProvider.onPeerConnected(() => {
|
|
9557
9729
|
this.onMobileConnected();
|
|
9558
9730
|
});
|
|
9731
|
+
// Register the shared ledger brand logos so <ion-icon> can resolve them by name.
|
|
9732
|
+
registerLedgerIcons();
|
|
9733
|
+
}
|
|
9734
|
+
/**
|
|
9735
|
+
* Apply the pinned network (if any) before the first render.
|
|
9736
|
+
*
|
|
9737
|
+
* When the host dApp provides a {@link network}, lock the selection to it and
|
|
9738
|
+
* skip straight to protocol selection so the user never sees — and cannot
|
|
9739
|
+
* change — the network. With no input, the modal opens on network selection.
|
|
9740
|
+
*/
|
|
9741
|
+
ngOnInit() {
|
|
9742
|
+
if (this.network) {
|
|
9743
|
+
this.selectedNetwork.set(this.network);
|
|
9744
|
+
this.currentStep.set(ConnectionStep.ProtocolSelection);
|
|
9745
|
+
}
|
|
9559
9746
|
}
|
|
9560
9747
|
/**
|
|
9561
9748
|
* Select a network option (internal, use selectNetworkAndProceed for UI)
|
|
@@ -9672,7 +9859,11 @@ class WalletConnectionModalComponent {
|
|
|
9672
9859
|
back() {
|
|
9673
9860
|
const currentStep = this.currentStep();
|
|
9674
9861
|
if (currentStep === ConnectionStep.ProtocolSelection) {
|
|
9675
|
-
|
|
9862
|
+
// When the network is pinned, protocol selection is the first reachable
|
|
9863
|
+
// step — there is no network step to return to.
|
|
9864
|
+
if (!this.network) {
|
|
9865
|
+
this.currentStep.set(ConnectionStep.NetworkSelection);
|
|
9866
|
+
}
|
|
9676
9867
|
}
|
|
9677
9868
|
else if (currentStep === ConnectionStep.ConnectionMethodSelection) {
|
|
9678
9869
|
this.currentStep.set(ConnectionStep.ProtocolSelection);
|
|
@@ -9752,7 +9943,7 @@ class WalletConnectionModalComponent {
|
|
|
9752
9943
|
await this.modalController.dismiss({ connected: true });
|
|
9753
9944
|
}
|
|
9754
9945
|
catch (error) {
|
|
9755
|
-
logger$
|
|
9946
|
+
logger$5.error('Connection failed', {
|
|
9756
9947
|
error: error instanceof Error ? error.message : String(error),
|
|
9757
9948
|
});
|
|
9758
9949
|
this.connectionError.set(error instanceof Error ? error.message : 'Connection failed. Please try again.');
|
|
@@ -9797,7 +9988,7 @@ class WalletConnectionModalComponent {
|
|
|
9797
9988
|
this.waitForWalletApproval();
|
|
9798
9989
|
}
|
|
9799
9990
|
catch (error) {
|
|
9800
|
-
logger$
|
|
9991
|
+
logger$5.error('Failed to create pairing offer', { error });
|
|
9801
9992
|
this.connectionError.set(error instanceof Error ? error.message : 'Failed to generate QR code');
|
|
9802
9993
|
this.isGeneratingOffer.set(false);
|
|
9803
9994
|
}
|
|
@@ -9810,7 +10001,7 @@ class WalletConnectionModalComponent {
|
|
|
9810
10001
|
try {
|
|
9811
10002
|
// This blocks until wallet scans QR, connects via Nostr, and user approves
|
|
9812
10003
|
const session = await this.p2pNativeProvider.waitForSessionApproval();
|
|
9813
|
-
logger$
|
|
10004
|
+
logger$5.info('Session approved by wallet', {
|
|
9814
10005
|
sessionId: session?.sessionId,
|
|
9815
10006
|
accounts: session?.accounts?.length,
|
|
9816
10007
|
});
|
|
@@ -9819,7 +10010,7 @@ class WalletConnectionModalComponent {
|
|
|
9819
10010
|
catch (error) {
|
|
9820
10011
|
// Only show error if we're still on the QR pairing step (not cancelled)
|
|
9821
10012
|
if (this.currentStep() === ConnectionStep.QrPairing) {
|
|
9822
|
-
logger$
|
|
10013
|
+
logger$5.error('Session approval failed', { error });
|
|
9823
10014
|
this.connectionError.set(error instanceof Error ? error.message : 'Session approval failed or rejected');
|
|
9824
10015
|
}
|
|
9825
10016
|
this.isWaitingForWallet.set(false);
|
|
@@ -9830,7 +10021,7 @@ class WalletConnectionModalComponent {
|
|
|
9830
10021
|
* Handle QR code expiry
|
|
9831
10022
|
*/
|
|
9832
10023
|
onQrExpired() {
|
|
9833
|
-
logger$
|
|
10024
|
+
logger$5.debug('QR code expired');
|
|
9834
10025
|
this.pairingOffer.set('');
|
|
9835
10026
|
this.isWaitingForWallet.set(false);
|
|
9836
10027
|
}
|
|
@@ -9846,7 +10037,7 @@ class WalletConnectionModalComponent {
|
|
|
9846
10037
|
* Handle successful mobile connection
|
|
9847
10038
|
*/
|
|
9848
10039
|
onMobileConnected() {
|
|
9849
|
-
logger$
|
|
10040
|
+
logger$5.info('Mobile wallet connected');
|
|
9850
10041
|
this.connected.emit({ protocol: 'mobile-native', method: 'qr' });
|
|
9851
10042
|
void this.modalController.dismiss({ connected: true });
|
|
9852
10043
|
}
|
|
@@ -9869,13 +10060,15 @@ class WalletConnectionModalComponent {
|
|
|
9869
10060
|
.__HSUITE_WALLET_EXTENSION__ === true);
|
|
9870
10061
|
}
|
|
9871
10062
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletConnectionModalComponent, deps: [{ token: i1$1.ModalController }, { token: UnifiedWalletService }], target: i0.ɵɵFactoryTarget.Component });
|
|
9872
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: WalletConnectionModalComponent, isStandalone: true, selector: "hsuite-wallet-connection-modal", inputs: { projectId: "projectId", appName: "appName", appDescription: "appDescription", walletUrl: "walletUrl" }, outputs: { connected: "connected" }, ngImport: i0, template: "<ion-content>\n <div class=\"modal-inner-content wallet-connection-modal\">\n <div class=\"header\">\n <div class=\"title\">\n <ion-button\n *ngIf=\"currentStep() > 1 && currentStep() !== ConnectionStep.ConnectionProgress\"\n (click)=\"back()\"\n fill=\"clear\"\n class=\"back-btn\"\n >\n <ion-icon slot=\"icon-only\" name=\"arrow-back-outline\"></ion-icon>\n </ion-button>\n <p>{{ stepTitle() }}</p>\n </div>\n <ion-button class=\"close-modal\" (click)=\"dismiss()\" fill=\"clear\" [disabled]=\"isConnecting()\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n\n <!-- Dynamic Step Progress Indicator -->\n <div class=\"dao-header\">\n <div class=\"step-progress\">\n <!-- Dynamic steps based on selected protocol path -->\n <ng-container *ngFor=\"let label of stepConfig().labels; let i = index\">\n <div class=\"step-indicator\">\n <div\n class=\"step-circle\"\n [class.active]=\"displayStep() === i + 1\"\n [class.completed]=\"displayStep() > i + 1\"\n [attr.title]=\"label\"\n >\n <ion-icon *ngIf=\"displayStep() > i + 1\" name=\"checkmark\"></ion-icon>\n <span *ngIf=\"displayStep() <= i + 1\">{{ i + 1 }}</span>\n </div>\n <!-- Step line between circles (not after the last one) -->\n <div\n class=\"step-line\"\n *ngIf=\"i < stepConfig().labels.length - 1\"\n [class.active]=\"displayStep() > i + 1\"\n ></div>\n </div>\n </ng-container>\n </div>\n </div>\n\n <div class=\"form\">\n <!-- Step 1: Network Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.NetworkSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Network</h3>\n <p>Select the network you want to connect to.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let network of networkOptions\"\n (click)=\"selectNetworkAndProceed(network.id)\"\n [class.selected]=\"selectedNetwork() === network.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"network.icon\"></ion-icon>\n </div>\n <h3>{{ network.name }}</h3>\n <p>{{ network.description }}</p>\n <ion-badge *ngIf=\"network.recommended\" color=\"success\">Recommended</ion-badge>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 2: Protocol Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ProtocolSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Connection Protocol</h3>\n <p>Choose how you want to connect your wallet.</p>\n </div>\n\n <div class=\"options-list direct-select\">\n <ion-card\n *ngFor=\"let protocol of protocolOptions()\"\n (click)=\"selectProtocolAndProceed(protocol.id)\"\n [class.selected]=\"selectedProtocol() === protocol.id\"\n button\n >\n <ion-card-content>\n <div class=\"protocol-header\">\n <div class=\"option-icon\">\n <ion-icon [name]=\"protocol.icon\"></ion-icon>\n </div>\n <div class=\"protocol-info\">\n <h3>{{ protocol.name }}</h3>\n <p>{{ protocol.description }}</p>\n </div>\n </div>\n\n <ul class=\"feature-list\">\n <li *ngFor=\"let feature of protocol.features\">\n <ion-icon name=\"checkmark-circle\" color=\"success\"></ion-icon>\n <span>{{ feature }}</span>\n </li>\n </ul>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 3: Connection Method Selection (HSuite Native only) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionMethodSelection\" class=\"step-content\">\n <hsuite-connection-method-step\n [methods]=\"connectionMethodOptions()\"\n [isMobile]=\"isMobile()\"\n [selectedMethod]=\"selectedConnectionMethod()\"\n (methodSelected)=\"onConnectionMethodSelected($event)\"\n ></hsuite-connection-method-step>\n </div>\n\n <!-- Step 3 (WalletConnect path): Ledger Selection (Direct Selection - click initiates connection) -->\n <div *ngIf=\"currentStep() === ConnectionStep.LedgerSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Blockchain</h3>\n <p>Select the blockchain ledger to connect with.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let ledger of ledgerOptions\"\n (click)=\"selectLedgerAndConnect(ledger.id)\"\n [class.selected]=\"selectedLedger() === ledger.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"ledger.icon\"></ion-icon>\n </div>\n <h3>{{ ledger.name }}</h3>\n <p>{{ ledger.description }}</p>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 5: QR Pairing (Mobile P2P - Nostr-first, single QR) -->\n <div *ngIf=\"currentStep() === ConnectionStep.QrPairing\" class=\"step-content\">\n <hsuite-qr-pairing-step\n [offerData]=\"pairingOffer()\"\n [appName]=\"appName\"\n [isLoading]=\"isGeneratingOffer()\"\n [isWaitingForWallet]=\"isWaitingForWallet()\"\n [isWaitingForApproval]=\"isWaitingForApproval()\"\n [errorMessage]=\"connectionError()\"\n (expired)=\"onQrExpired()\"\n (refreshRequested)=\"onQrRefresh()\"\n (cancelled)=\"back()\"\n ></hsuite-qr-pairing-step>\n </div>\n\n <!-- Step 6: Connection Progress -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionProgress\" class=\"step-content\">\n <div class=\"membership-section connection-progress\">\n <div *ngIf=\"isConnecting()\" class=\"status-info\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <h3>Connecting...</h3>\n <p *ngIf=\"selectedProtocol() === 'walletconnect'\">\n Please approve the connection in your wallet app or scan the QR code.\n </p>\n <p *ngIf=\"selectedProtocol() === 'hsuite-native'\">Opening HSuite Wallet...</p>\n </div>\n\n <div *ngIf=\"connectionError()\" class=\"membership-section\">\n <div class=\"error-section\">\n <ion-item lines=\"none\">\n <ion-icon slot=\"start\" name=\"alert-circle-outline\" color=\"danger\"></ion-icon>\n <ion-label>\n <h3>Connection Failed</h3>\n <p>{{ connectionError() }}</p>\n </ion-label>\n </ion-item>\n </div>\n <ion-button expand=\"block\" fill=\"outline\" (click)=\"back()\" class=\"ion-margin-top\">\n Try Again\n </ion-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer removed: All steps now use direct selection (click to proceed) -->\n </div>\n</ion-content>\n", styles: [":host{display:flex;align-items:center;justify-content:center;height:100%;width:100%}ion-content{--background: transparent;--padding-start: 0;--padding-end: 0;--padding-top: 0;--padding-bottom: 0;height:100vh;width:100%;display:flex!important;align-items:center!important;justify-content:center!important}ion-content::part(scroll){display:flex;align-items:center;justify-content:center;min-height:100%;width:100%}.modal-inner-content{width:90%;max-width:600px;max-height:80vh;background:var(--glass-background, rgba(var(--ion-color-dark-rgb), .95));border-radius:var(--border-radius-xl, 16px);padding:0;display:flex;flex-direction:column;box-shadow:var(--shadow-2xl);border:1px solid var(--border-flat, rgba(var(--ion-color-light-rgb), .1));overflow:hidden;animation:modalFadeIn .3s ease}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-lg, 16px) var(--space-xl, 20px);background:transparent;flex-shrink:0}.header .title{display:flex;align-items:center;gap:var(--space-sm, 8px);flex:1}.header .title p{margin:0;font-size:var(--font-size-2xl, 24px);font-weight:var(--font-weight-bold, 700);color:var(--ion-text-color);line-height:1.2}.header .back-btn{--padding-start: 8px;--padding-end: 8px;margin:0}.header .back-btn ion-icon{font-size:22px;color:var(--ion-color-primary)}.header .close-modal{--padding-start: 8px;--padding-end: 8px;margin:0;cursor:pointer}.header .close-modal ion-icon{font-size:24px;color:var(--ion-text-color)}.header .close-modal:hover:not(:disabled){transform:scale(1.05)}.header .close-modal:active:not(:disabled){transform:scale(.95)}.form{flex:1;overflow-y:auto;padding:0 var(--space-xl, 20px) var(--space-xl, 20px) var(--space-xl, 20px);background:transparent}.form::-webkit-scrollbar{width:8px}.form::-webkit-scrollbar-track{background:var(--surface-flat, rgba(var(--ion-color-light-rgb), .05));border-radius:4px}.form::-webkit-scrollbar-thumb{background:rgba(var(--ion-color-light-rgb),.2);border-radius:4px}.form::-webkit-scrollbar-thumb:hover{background:rgba(var(--ion-color-light-rgb),.3)}@media(min-width:768px){.modal-inner-content{width:80%;max-width:700px;border-radius:20px}.header{padding:20px 24px}.dao-header{margin:0 24px 24px}.form{padding:0 24px 24px}}@media(max-width:480px){.modal-inner-content{width:95%;max-height:85vh}.header{padding:14px 16px}.dao-header{margin:0 16px 20px}.form{padding:0 16px 16px}}.dao-header{margin:0 var(--space-xl, 20px) var(--token-space-6, 24px) var(--space-xl, 20px);padding:var(--token-space-4, 16px);background:var(--token-surface-2, var(--surface-flat, rgba(var(--ion-color-light-rgb), .05)));border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);box-shadow:var(--token-elevation-1, var(--shadow-sm));flex-shrink:0;animation:fadeIn var(--token-transition-slow, .3s) ease}.dao-header .step-progress{display:flex;align-items:center;justify-content:center}.dao-header .step-progress .step-indicator{display:flex;align-items:center}.dao-header .step-progress .step-indicator .step-circle{width:36px;height:36px;border-radius:var(--token-radius-full, 50%);display:flex;align-items:center;justify-content:center;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .1));color:var(--ion-text-color);font-weight:var(--token-font-weight-semibold, 600);font-size:var(--token-font-size-2, 14px);border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));transition:all var(--token-transition-slow, .3s) var(--token-ease-in-out, cubic-bezier(.4, 0, .2, 1))}.dao-header .step-progress .step-indicator .step-circle.active{background:var(--ion-color-primary);color:var(--ion-color-primary-contrast);border-color:var(--ion-color-primary);transform:scale(1.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.2)}.dao-header .step-progress .step-indicator .step-circle.completed{background:var(--ion-color-success);color:var(--ion-color-success-contrast);border-color:var(--ion-color-success);transform:scale(1.05)}.dao-header .step-progress .step-indicator .step-circle:hover:not(.active):not(.completed){transform:scale(1.05);background:rgba(var(--ion-color-light-rgb),.15)}.dao-header .step-progress .step-indicator .step-circle ion-icon{font-size:20px}.dao-header .step-progress .step-indicator .step-line{width:40px;height:var(--token-border-width-2, 2px);background:var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));margin:0 var(--token-space-2, 8px);transition:all var(--token-transition-slow, .3s) ease}.dao-header .step-progress .step-indicator .step-line.active{background:var(--ion-color-success);box-shadow:0 0 8px rgba(var(--ion-color-success-rgb),.5)}.membership-section{margin-bottom:var(--token-space-6, 24px);animation:fadeIn var(--token-transition-slow, .3s) ease}.membership-section .explanation{margin-bottom:var(--token-space-4, 16px)}.membership-section .explanation h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-text-color);line-height:var(--token-line-height-tight, 1.3);margin:0 0 var(--token-space-2, 8px) 0}.membership-section .explanation p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);line-height:var(--token-line-height-normal, 1.4);margin:0}.membership-section .options-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--token-space-3, 12px)}.membership-section .options-grid ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-grid ion-card:hover{transform:translateY(-2px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-grid ion-card:active{transform:translateY(0)}.membership-section .options-grid ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-grid ion-card ion-card-content{text-align:center;padding:var(--token-space-5, 20px)}.membership-section .options-grid ion-card ion-card-content .option-icon{margin-bottom:var(--token-space-3, 12px)}.membership-section .options-grid ion-card ion-card-content .option-icon ion-icon{font-size:40px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-grid ion-card ion-card-content h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 6px) 0;color:var(--ion-text-color)}.membership-section .options-grid ion-card ion-card-content p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0 0 var(--token-space-2, 8px) 0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-grid ion-card:hover .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section .options-list{display:flex;flex-direction:column;gap:var(--token-space-3, 12px)}.membership-section .options-list ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-list ion-card:hover{transform:translate(4px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-list ion-card:active{transform:translate(2px)}.membership-section .options-list ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-list ion-card ion-card-content{padding:var(--token-space-4, 16px)}.membership-section .options-list ion-card ion-card-content .protocol-header{display:flex;align-items:center;gap:var(--token-space-3, 12px);margin-bottom:var(--token-space-3, 12px)}.membership-section .options-list ion-card ion-card-content .protocol-header .option-icon ion-icon{font-size:36px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info{flex:1;min-width:0}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-1, 4px) 0;color:var(--ion-text-color)}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-list ion-card ion-card-content .feature-list{list-style:none;padding:0;margin:0}.membership-section .options-list ion-card ion-card-content .feature-list li{display:flex;align-items:center;gap:var(--token-space-2, 8px);padding:var(--token-space-1, 4px) 0;font-size:var(--token-font-size-1, 13px);color:var(--ion-text-color);line-height:var(--token-line-height-normal, 1.4)}.membership-section .options-list ion-card ion-card-content .feature-list li ion-icon{font-size:var(--token-space-4, 16px);flex-shrink:0;color:var(--ion-color-success)}.membership-section .options-list ion-card:hover .protocol-header .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section.connection-progress{display:flex;justify-content:center;align-items:center;min-height:200px}.membership-section.connection-progress .status-info{text-align:center;padding:var(--token-space-6, 24px)}.membership-section.connection-progress .status-info ion-spinner{font-size:48px;margin-bottom:var(--token-space-4, 16px);color:var(--ion-color-primary)}.membership-section.connection-progress .status-info h3{font-size:var(--token-font-size-4, 18px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 8px) 0;color:var(--ion-text-color)}.membership-section.connection-progress .status-info p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);margin:0;max-width:300px;line-height:var(--token-line-height-normal, 1.4)}.membership-section .error-section{padding:var(--token-space-3, 12px);background:rgba(var(--ion-color-danger-rgb),.1);border:1px solid rgba(var(--ion-color-danger-rgb),.2);border-radius:var(--token-radius-2, 8px)}.membership-section .error-section ion-item{--background: transparent;--padding-start: 0;--inner-padding-end: 0}.membership-section .error-section ion-item ion-icon{font-size:var(--token-space-6, 24px)}.membership-section .error-section ion-item ion-label h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-color-danger);margin:0 0 var(--token-space-1, 4px) 0}.membership-section .error-section ion-item ion-label p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.step-content{animation:stepFadeIn var(--token-transition-normal, .25s) var(--token-ease-out, cubic-bezier(0, 0, .2, 1))}.direct-select ion-card{position:relative;overflow:hidden}.direct-select ion-card:after{content:\"\";position:absolute;right:12px;top:50%;transform:translateY(-50%) translate(0);width:0;height:0;border-left:6px solid var(--ion-color-primary);border-top:5px solid transparent;border-bottom:5px solid transparent;opacity:0;transition:all var(--token-transition-fast, .15s) ease}.direct-select ion-card:hover:after{opacity:.6;transform:translateY(-50%) translate(4px)}.direct-select ion-card:hover{border-color:rgba(var(--ion-color-primary-rgb),.4)}.direct-select ion-card:hover .option-icon ion-icon{color:var(--ion-color-primary)}.direct-select ion-card:active{transform:scale(.98);background:rgba(var(--ion-color-primary-rgb),.15)!important}.direct-select.options-grid ion-card:after{right:8px;top:16px;transform:translateY(0) translate(0)}.direct-select.options-grid ion-card:hover:after{transform:translateY(0) translate(4px)}@keyframes fadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),.4)}70%{box-shadow:0 0 0 8px rgba(var(--ion-color-primary-rgb),0)}to{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),0)}}.step-progress .step-indicator .step-circle.active{animation:stepPulse 1.5s ease-out infinite}@media(max-width:768px){.membership-section .options-grid{grid-template-columns:1fr}.dao-header .step-progress .step-indicator .step-circle{width:28px;height:28px;font-size:12px}.dao-header .step-progress .step-indicator .step-line{width:24px}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}@keyframes fadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%,to{box-shadow:none}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: IonicModule }, { kind: "component", type: i1$1.IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { kind: "component", type: i1$1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i1$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i1$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i1$1.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i1$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i1$1.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i1$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i1$1.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: ConnectionMethodStepComponent, selector: "hsuite-connection-method-step", inputs: ["methods", "isMobile", "selectedMethod"], outputs: ["methodSelected"] }, { kind: "component", type: QrPairingStepComponent, selector: "hsuite-qr-pairing-step", inputs: ["offerData", "appName", "isLoading", "isWaitingForWallet", "isWaitingForApproval", "errorMessage", "expirySeconds"], outputs: ["expired", "connected", "cancelled", "refreshRequested"] }] });
|
|
10063
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: WalletConnectionModalComponent, isStandalone: true, selector: "hsuite-wallet-connection-modal", inputs: { projectId: "projectId", network: "network", appName: "appName", appDescription: "appDescription", walletUrl: "walletUrl" }, outputs: { connected: "connected" }, ngImport: i0, template: "<ion-content>\n <div class=\"modal-inner-content wallet-connection-modal\">\n <div class=\"header\">\n <div class=\"title\">\n <ion-button *ngIf=\"canGoBack()\" (click)=\"back()\" fill=\"clear\" class=\"back-btn\">\n <ion-icon slot=\"icon-only\" name=\"arrow-back-outline\"></ion-icon>\n </ion-button>\n <p>{{ stepTitle() }}</p>\n </div>\n <ion-button class=\"close-modal\" (click)=\"dismiss()\" fill=\"clear\" [disabled]=\"isConnecting()\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n\n <!-- Dynamic Step Progress Indicator -->\n <div class=\"dao-header\">\n <div class=\"step-progress\">\n <!-- Dynamic steps based on selected protocol path -->\n <ng-container *ngFor=\"let label of stepConfig().labels; let i = index\">\n <div class=\"step-indicator\">\n <div\n class=\"step-circle\"\n [class.active]=\"displayStep() === i + 1\"\n [class.completed]=\"displayStep() > i + 1\"\n [attr.title]=\"label\"\n >\n <ion-icon *ngIf=\"displayStep() > i + 1\" name=\"checkmark\"></ion-icon>\n <span *ngIf=\"displayStep() <= i + 1\">{{ i + 1 }}</span>\n </div>\n <!-- Step line between circles (not after the last one) -->\n <div\n class=\"step-line\"\n *ngIf=\"i < stepConfig().labels.length - 1\"\n [class.active]=\"displayStep() > i + 1\"\n ></div>\n </div>\n </ng-container>\n </div>\n </div>\n\n <div class=\"form\">\n <!-- Step 1: Network Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.NetworkSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Network</h3>\n <p>Select the network you want to connect to.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let network of networkOptions\"\n (click)=\"selectNetworkAndProceed(network.id)\"\n [class.selected]=\"selectedNetwork() === network.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"network.icon\"></ion-icon>\n </div>\n <h3>{{ network.name }}</h3>\n <p>{{ network.description }}</p>\n <ion-badge *ngIf=\"network.recommended\" color=\"success\">Recommended</ion-badge>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 2: Protocol Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ProtocolSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Connection Protocol</h3>\n <p>Choose how you want to connect your wallet.</p>\n </div>\n\n <div class=\"options-list direct-select\">\n <ion-card\n *ngFor=\"let protocol of protocolOptions()\"\n (click)=\"selectProtocolAndProceed(protocol.id)\"\n [class.selected]=\"selectedProtocol() === protocol.id\"\n button\n >\n <ion-card-content>\n <div class=\"protocol-header\">\n <div class=\"option-icon\">\n <ion-icon [name]=\"protocol.icon\"></ion-icon>\n </div>\n <div class=\"protocol-info\">\n <h3>{{ protocol.name }}</h3>\n <p>{{ protocol.description }}</p>\n </div>\n </div>\n\n <ul class=\"feature-list\">\n <li *ngFor=\"let feature of protocol.features\">\n <ion-icon name=\"checkmark-circle\" color=\"success\"></ion-icon>\n <span>{{ feature }}</span>\n </li>\n </ul>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 3: Connection Method Selection (HSuite Native only) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionMethodSelection\" class=\"step-content\">\n <hsuite-connection-method-step\n [methods]=\"connectionMethodOptions()\"\n [isMobile]=\"isMobile()\"\n [selectedMethod]=\"selectedConnectionMethod()\"\n (methodSelected)=\"onConnectionMethodSelected($event)\"\n ></hsuite-connection-method-step>\n </div>\n\n <!-- Step 3 (WalletConnect path): Ledger Selection (Direct Selection - click initiates connection) -->\n <div *ngIf=\"currentStep() === ConnectionStep.LedgerSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Blockchain</h3>\n <p>Select the blockchain ledger to connect with.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let ledger of ledgerOptions\"\n (click)=\"selectLedgerAndConnect(ledger.id)\"\n [class.selected]=\"selectedLedger() === ledger.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"ledger.icon\"></ion-icon>\n </div>\n <h3>{{ ledger.name }}</h3>\n <p>{{ ledger.description }}</p>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 5: QR Pairing (Mobile P2P - Nostr-first, single QR) -->\n <div *ngIf=\"currentStep() === ConnectionStep.QrPairing\" class=\"step-content\">\n <hsuite-qr-pairing-step\n [offerData]=\"pairingOffer()\"\n [appName]=\"appName\"\n [isLoading]=\"isGeneratingOffer()\"\n [isWaitingForWallet]=\"isWaitingForWallet()\"\n [isWaitingForApproval]=\"isWaitingForApproval()\"\n [errorMessage]=\"connectionError()\"\n (expired)=\"onQrExpired()\"\n (refreshRequested)=\"onQrRefresh()\"\n (cancelled)=\"back()\"\n ></hsuite-qr-pairing-step>\n </div>\n\n <!-- Step 6: Connection Progress -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionProgress\" class=\"step-content\">\n <div class=\"membership-section connection-progress\">\n <div *ngIf=\"isConnecting()\" class=\"status-info\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <h3>Connecting...</h3>\n <p *ngIf=\"selectedProtocol() === 'walletconnect'\">\n Please approve the connection in your wallet app or scan the QR code.\n </p>\n <p *ngIf=\"selectedProtocol() === 'hsuite-native'\">Opening HSuite Wallet...</p>\n </div>\n\n <div *ngIf=\"connectionError()\" class=\"membership-section\">\n <div class=\"error-section\">\n <ion-item lines=\"none\">\n <ion-icon slot=\"start\" name=\"alert-circle-outline\" color=\"danger\"></ion-icon>\n <ion-label>\n <h3>Connection Failed</h3>\n <p>{{ connectionError() }}</p>\n </ion-label>\n </ion-item>\n </div>\n <ion-button expand=\"block\" fill=\"outline\" (click)=\"back()\" class=\"ion-margin-top\">\n Try Again\n </ion-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer removed: All steps now use direct selection (click to proceed) -->\n </div>\n</ion-content>\n", styles: [":host{display:flex;align-items:center;justify-content:center;height:100%;width:100%}ion-content{--background: transparent;--padding-start: 0;--padding-end: 0;--padding-top: 0;--padding-bottom: 0;height:100vh;width:100%;display:flex!important;align-items:center!important;justify-content:center!important}ion-content::part(scroll){display:flex;align-items:center;justify-content:center;min-height:100%;width:100%}.modal-inner-content{width:90%;max-width:600px;max-height:80vh;background:var(--glass-background, rgba(var(--ion-color-dark-rgb), .95));border-radius:var(--border-radius-xl, 16px);padding:0;display:flex;flex-direction:column;box-shadow:var(--shadow-2xl);border:1px solid var(--border-flat, rgba(var(--ion-color-light-rgb), .1));overflow:hidden;animation:modalFadeIn .3s ease}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-lg, 16px) var(--space-xl, 20px);background:transparent;flex-shrink:0}.header .title{display:flex;align-items:center;gap:var(--space-sm, 8px);flex:1}.header .title p{margin:0;font-size:var(--font-size-2xl, 24px);font-weight:var(--font-weight-bold, 700);color:var(--ion-text-color);line-height:1.2}.header .back-btn{--padding-start: 8px;--padding-end: 8px;margin:0}.header .back-btn ion-icon{font-size:22px;color:var(--ion-color-primary)}.header .close-modal{--padding-start: 8px;--padding-end: 8px;margin:0;cursor:pointer}.header .close-modal ion-icon{font-size:24px;color:var(--ion-text-color)}.header .close-modal:hover:not(:disabled){transform:scale(1.05)}.header .close-modal:active:not(:disabled){transform:scale(.95)}.form{flex:1;overflow-y:auto;padding:0 var(--space-xl, 20px) var(--space-xl, 20px) var(--space-xl, 20px);background:transparent}.form::-webkit-scrollbar{width:8px}.form::-webkit-scrollbar-track{background:var(--surface-flat, rgba(var(--ion-color-light-rgb), .05));border-radius:4px}.form::-webkit-scrollbar-thumb{background:rgba(var(--ion-color-light-rgb),.2);border-radius:4px}.form::-webkit-scrollbar-thumb:hover{background:rgba(var(--ion-color-light-rgb),.3)}@media(min-width:768px){.modal-inner-content{width:80%;max-width:700px;border-radius:20px}.header{padding:20px 24px}.dao-header{margin:0 24px 24px}.form{padding:0 24px 24px}}@media(max-width:480px){.modal-inner-content{width:95%;max-height:85vh}.header{padding:14px 16px}.dao-header{margin:0 16px 20px}.form{padding:0 16px 16px}}.dao-header{margin:0 var(--space-xl, 20px) var(--token-space-6, 24px) var(--space-xl, 20px);padding:var(--token-space-4, 16px);background:var(--token-surface-2, var(--surface-flat, rgba(var(--ion-color-light-rgb), .05)));border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);box-shadow:var(--token-elevation-1, var(--shadow-sm));flex-shrink:0;animation:fadeIn var(--token-transition-slow, .3s) ease}.dao-header .step-progress{display:flex;align-items:center;justify-content:center}.dao-header .step-progress .step-indicator{display:flex;align-items:center}.dao-header .step-progress .step-indicator .step-circle{width:36px;height:36px;border-radius:var(--token-radius-full, 50%);display:flex;align-items:center;justify-content:center;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .1));color:var(--ion-text-color);font-weight:var(--token-font-weight-semibold, 600);font-size:var(--token-font-size-2, 14px);border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));transition:all var(--token-transition-slow, .3s) var(--token-ease-in-out, cubic-bezier(.4, 0, .2, 1))}.dao-header .step-progress .step-indicator .step-circle.active{background:var(--ion-color-primary);color:var(--ion-color-primary-contrast);border-color:var(--ion-color-primary);transform:scale(1.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.2)}.dao-header .step-progress .step-indicator .step-circle.completed{background:var(--ion-color-success);color:var(--ion-color-success-contrast);border-color:var(--ion-color-success);transform:scale(1.05)}.dao-header .step-progress .step-indicator .step-circle:hover:not(.active):not(.completed){transform:scale(1.05);background:rgba(var(--ion-color-light-rgb),.15)}.dao-header .step-progress .step-indicator .step-circle ion-icon{font-size:20px}.dao-header .step-progress .step-indicator .step-line{width:40px;height:var(--token-border-width-2, 2px);background:var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));margin:0 var(--token-space-2, 8px);transition:all var(--token-transition-slow, .3s) ease}.dao-header .step-progress .step-indicator .step-line.active{background:var(--ion-color-success);box-shadow:0 0 8px rgba(var(--ion-color-success-rgb),.5)}.membership-section{margin-bottom:var(--token-space-6, 24px);animation:fadeIn var(--token-transition-slow, .3s) ease}.membership-section .explanation{margin-bottom:var(--token-space-4, 16px)}.membership-section .explanation h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-text-color);line-height:var(--token-line-height-tight, 1.3);margin:0 0 var(--token-space-2, 8px) 0}.membership-section .explanation p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);line-height:var(--token-line-height-normal, 1.4);margin:0}.membership-section .options-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--token-space-3, 12px)}.membership-section .options-grid ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-grid ion-card:hover{transform:translateY(-2px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-grid ion-card:active{transform:translateY(0)}.membership-section .options-grid ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-grid ion-card ion-card-content{text-align:center;padding:var(--token-space-5, 20px)}.membership-section .options-grid ion-card ion-card-content .option-icon{margin-bottom:var(--token-space-3, 12px)}.membership-section .options-grid ion-card ion-card-content .option-icon ion-icon{font-size:40px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-grid ion-card ion-card-content h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 6px) 0;color:var(--ion-text-color)}.membership-section .options-grid ion-card ion-card-content p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0 0 var(--token-space-2, 8px) 0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-grid ion-card:hover .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section .options-list{display:flex;flex-direction:column;gap:var(--token-space-3, 12px)}.membership-section .options-list ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-list ion-card:hover{transform:translate(4px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-list ion-card:active{transform:translate(2px)}.membership-section .options-list ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-list ion-card ion-card-content{padding:var(--token-space-4, 16px)}.membership-section .options-list ion-card ion-card-content .protocol-header{display:flex;align-items:center;gap:var(--token-space-3, 12px);margin-bottom:var(--token-space-3, 12px)}.membership-section .options-list ion-card ion-card-content .protocol-header .option-icon ion-icon{font-size:36px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info{flex:1;min-width:0}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-1, 4px) 0;color:var(--ion-text-color)}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-list ion-card ion-card-content .feature-list{list-style:none;padding:0;margin:0}.membership-section .options-list ion-card ion-card-content .feature-list li{display:flex;align-items:center;gap:var(--token-space-2, 8px);padding:var(--token-space-1, 4px) 0;font-size:var(--token-font-size-1, 13px);color:var(--ion-text-color);line-height:var(--token-line-height-normal, 1.4)}.membership-section .options-list ion-card ion-card-content .feature-list li ion-icon{font-size:var(--token-space-4, 16px);flex-shrink:0;color:var(--ion-color-success)}.membership-section .options-list ion-card:hover .protocol-header .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section.connection-progress{display:flex;justify-content:center;align-items:center;min-height:200px}.membership-section.connection-progress .status-info{text-align:center;padding:var(--token-space-6, 24px)}.membership-section.connection-progress .status-info ion-spinner{font-size:48px;margin-bottom:var(--token-space-4, 16px);color:var(--ion-color-primary)}.membership-section.connection-progress .status-info h3{font-size:var(--token-font-size-4, 18px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 8px) 0;color:var(--ion-text-color)}.membership-section.connection-progress .status-info p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);margin:0;max-width:300px;line-height:var(--token-line-height-normal, 1.4)}.membership-section .error-section{padding:var(--token-space-3, 12px);background:rgba(var(--ion-color-danger-rgb),.1);border:1px solid rgba(var(--ion-color-danger-rgb),.2);border-radius:var(--token-radius-2, 8px)}.membership-section .error-section ion-item{--background: transparent;--padding-start: 0;--inner-padding-end: 0}.membership-section .error-section ion-item ion-icon{font-size:var(--token-space-6, 24px)}.membership-section .error-section ion-item ion-label h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-color-danger);margin:0 0 var(--token-space-1, 4px) 0}.membership-section .error-section ion-item ion-label p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.step-content{animation:stepFadeIn var(--token-transition-normal, .25s) var(--token-ease-out, cubic-bezier(0, 0, .2, 1))}.direct-select ion-card{position:relative;overflow:hidden}.direct-select ion-card:after{content:\"\";position:absolute;right:12px;top:50%;transform:translateY(-50%) translate(0);width:0;height:0;border-left:6px solid var(--ion-color-primary);border-top:5px solid transparent;border-bottom:5px solid transparent;opacity:0;transition:all var(--token-transition-fast, .15s) ease}.direct-select ion-card:hover:after{opacity:.6;transform:translateY(-50%) translate(4px)}.direct-select ion-card:hover{border-color:rgba(var(--ion-color-primary-rgb),.4)}.direct-select ion-card:hover .option-icon ion-icon{color:var(--ion-color-primary)}.direct-select ion-card:active{transform:scale(.98);background:rgba(var(--ion-color-primary-rgb),.15)!important}.direct-select.options-grid ion-card:after{right:8px;top:16px;transform:translateY(0) translate(0)}.direct-select.options-grid ion-card:hover:after{transform:translateY(0) translate(4px)}@keyframes fadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),.4)}70%{box-shadow:0 0 0 8px rgba(var(--ion-color-primary-rgb),0)}to{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),0)}}.step-progress .step-indicator .step-circle.active{animation:stepPulse 1.5s ease-out infinite}@media(max-width:768px){.membership-section .options-grid{grid-template-columns:1fr}.dao-header .step-progress .step-indicator .step-circle{width:28px;height:28px;font-size:12px}.dao-header .step-progress .step-indicator .step-line{width:24px}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}@keyframes fadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%,to{box-shadow:none}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: IonicModule }, { kind: "component", type: i1$1.IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { kind: "component", type: i1$1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i1$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i1$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i1$1.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i1$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i1$1.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i1$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i1$1.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: ConnectionMethodStepComponent, selector: "hsuite-connection-method-step", inputs: ["methods", "isMobile", "selectedMethod"], outputs: ["methodSelected"] }, { kind: "component", type: QrPairingStepComponent, selector: "hsuite-qr-pairing-step", inputs: ["offerData", "appName", "isLoading", "isWaitingForWallet", "isWaitingForApproval", "errorMessage", "expirySeconds"], outputs: ["expired", "connected", "cancelled", "refreshRequested"] }] });
|
|
9873
10064
|
}
|
|
9874
10065
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletConnectionModalComponent, decorators: [{
|
|
9875
10066
|
type: Component,
|
|
9876
|
-
args: [{ selector: 'hsuite-wallet-connection-modal', standalone: true, imports: [CommonModule, IonicModule, ConnectionMethodStepComponent, QrPairingStepComponent], template: "<ion-content>\n <div class=\"modal-inner-content wallet-connection-modal\">\n <div class=\"header\">\n <div class=\"title\">\n <ion-button\n *ngIf=\"currentStep() > 1 && currentStep() !== ConnectionStep.ConnectionProgress\"\n (click)=\"back()\"\n fill=\"clear\"\n class=\"back-btn\"\n >\n <ion-icon slot=\"icon-only\" name=\"arrow-back-outline\"></ion-icon>\n </ion-button>\n <p>{{ stepTitle() }}</p>\n </div>\n <ion-button class=\"close-modal\" (click)=\"dismiss()\" fill=\"clear\" [disabled]=\"isConnecting()\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n\n <!-- Dynamic Step Progress Indicator -->\n <div class=\"dao-header\">\n <div class=\"step-progress\">\n <!-- Dynamic steps based on selected protocol path -->\n <ng-container *ngFor=\"let label of stepConfig().labels; let i = index\">\n <div class=\"step-indicator\">\n <div\n class=\"step-circle\"\n [class.active]=\"displayStep() === i + 1\"\n [class.completed]=\"displayStep() > i + 1\"\n [attr.title]=\"label\"\n >\n <ion-icon *ngIf=\"displayStep() > i + 1\" name=\"checkmark\"></ion-icon>\n <span *ngIf=\"displayStep() <= i + 1\">{{ i + 1 }}</span>\n </div>\n <!-- Step line between circles (not after the last one) -->\n <div\n class=\"step-line\"\n *ngIf=\"i < stepConfig().labels.length - 1\"\n [class.active]=\"displayStep() > i + 1\"\n ></div>\n </div>\n </ng-container>\n </div>\n </div>\n\n <div class=\"form\">\n <!-- Step 1: Network Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.NetworkSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Network</h3>\n <p>Select the network you want to connect to.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let network of networkOptions\"\n (click)=\"selectNetworkAndProceed(network.id)\"\n [class.selected]=\"selectedNetwork() === network.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"network.icon\"></ion-icon>\n </div>\n <h3>{{ network.name }}</h3>\n <p>{{ network.description }}</p>\n <ion-badge *ngIf=\"network.recommended\" color=\"success\">Recommended</ion-badge>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 2: Protocol Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ProtocolSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Connection Protocol</h3>\n <p>Choose how you want to connect your wallet.</p>\n </div>\n\n <div class=\"options-list direct-select\">\n <ion-card\n *ngFor=\"let protocol of protocolOptions()\"\n (click)=\"selectProtocolAndProceed(protocol.id)\"\n [class.selected]=\"selectedProtocol() === protocol.id\"\n button\n >\n <ion-card-content>\n <div class=\"protocol-header\">\n <div class=\"option-icon\">\n <ion-icon [name]=\"protocol.icon\"></ion-icon>\n </div>\n <div class=\"protocol-info\">\n <h3>{{ protocol.name }}</h3>\n <p>{{ protocol.description }}</p>\n </div>\n </div>\n\n <ul class=\"feature-list\">\n <li *ngFor=\"let feature of protocol.features\">\n <ion-icon name=\"checkmark-circle\" color=\"success\"></ion-icon>\n <span>{{ feature }}</span>\n </li>\n </ul>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 3: Connection Method Selection (HSuite Native only) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionMethodSelection\" class=\"step-content\">\n <hsuite-connection-method-step\n [methods]=\"connectionMethodOptions()\"\n [isMobile]=\"isMobile()\"\n [selectedMethod]=\"selectedConnectionMethod()\"\n (methodSelected)=\"onConnectionMethodSelected($event)\"\n ></hsuite-connection-method-step>\n </div>\n\n <!-- Step 3 (WalletConnect path): Ledger Selection (Direct Selection - click initiates connection) -->\n <div *ngIf=\"currentStep() === ConnectionStep.LedgerSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Blockchain</h3>\n <p>Select the blockchain ledger to connect with.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let ledger of ledgerOptions\"\n (click)=\"selectLedgerAndConnect(ledger.id)\"\n [class.selected]=\"selectedLedger() === ledger.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"ledger.icon\"></ion-icon>\n </div>\n <h3>{{ ledger.name }}</h3>\n <p>{{ ledger.description }}</p>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 5: QR Pairing (Mobile P2P - Nostr-first, single QR) -->\n <div *ngIf=\"currentStep() === ConnectionStep.QrPairing\" class=\"step-content\">\n <hsuite-qr-pairing-step\n [offerData]=\"pairingOffer()\"\n [appName]=\"appName\"\n [isLoading]=\"isGeneratingOffer()\"\n [isWaitingForWallet]=\"isWaitingForWallet()\"\n [isWaitingForApproval]=\"isWaitingForApproval()\"\n [errorMessage]=\"connectionError()\"\n (expired)=\"onQrExpired()\"\n (refreshRequested)=\"onQrRefresh()\"\n (cancelled)=\"back()\"\n ></hsuite-qr-pairing-step>\n </div>\n\n <!-- Step 6: Connection Progress -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionProgress\" class=\"step-content\">\n <div class=\"membership-section connection-progress\">\n <div *ngIf=\"isConnecting()\" class=\"status-info\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <h3>Connecting...</h3>\n <p *ngIf=\"selectedProtocol() === 'walletconnect'\">\n Please approve the connection in your wallet app or scan the QR code.\n </p>\n <p *ngIf=\"selectedProtocol() === 'hsuite-native'\">Opening HSuite Wallet...</p>\n </div>\n\n <div *ngIf=\"connectionError()\" class=\"membership-section\">\n <div class=\"error-section\">\n <ion-item lines=\"none\">\n <ion-icon slot=\"start\" name=\"alert-circle-outline\" color=\"danger\"></ion-icon>\n <ion-label>\n <h3>Connection Failed</h3>\n <p>{{ connectionError() }}</p>\n </ion-label>\n </ion-item>\n </div>\n <ion-button expand=\"block\" fill=\"outline\" (click)=\"back()\" class=\"ion-margin-top\">\n Try Again\n </ion-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer removed: All steps now use direct selection (click to proceed) -->\n </div>\n</ion-content>\n", styles: [":host{display:flex;align-items:center;justify-content:center;height:100%;width:100%}ion-content{--background: transparent;--padding-start: 0;--padding-end: 0;--padding-top: 0;--padding-bottom: 0;height:100vh;width:100%;display:flex!important;align-items:center!important;justify-content:center!important}ion-content::part(scroll){display:flex;align-items:center;justify-content:center;min-height:100%;width:100%}.modal-inner-content{width:90%;max-width:600px;max-height:80vh;background:var(--glass-background, rgba(var(--ion-color-dark-rgb), .95));border-radius:var(--border-radius-xl, 16px);padding:0;display:flex;flex-direction:column;box-shadow:var(--shadow-2xl);border:1px solid var(--border-flat, rgba(var(--ion-color-light-rgb), .1));overflow:hidden;animation:modalFadeIn .3s ease}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-lg, 16px) var(--space-xl, 20px);background:transparent;flex-shrink:0}.header .title{display:flex;align-items:center;gap:var(--space-sm, 8px);flex:1}.header .title p{margin:0;font-size:var(--font-size-2xl, 24px);font-weight:var(--font-weight-bold, 700);color:var(--ion-text-color);line-height:1.2}.header .back-btn{--padding-start: 8px;--padding-end: 8px;margin:0}.header .back-btn ion-icon{font-size:22px;color:var(--ion-color-primary)}.header .close-modal{--padding-start: 8px;--padding-end: 8px;margin:0;cursor:pointer}.header .close-modal ion-icon{font-size:24px;color:var(--ion-text-color)}.header .close-modal:hover:not(:disabled){transform:scale(1.05)}.header .close-modal:active:not(:disabled){transform:scale(.95)}.form{flex:1;overflow-y:auto;padding:0 var(--space-xl, 20px) var(--space-xl, 20px) var(--space-xl, 20px);background:transparent}.form::-webkit-scrollbar{width:8px}.form::-webkit-scrollbar-track{background:var(--surface-flat, rgba(var(--ion-color-light-rgb), .05));border-radius:4px}.form::-webkit-scrollbar-thumb{background:rgba(var(--ion-color-light-rgb),.2);border-radius:4px}.form::-webkit-scrollbar-thumb:hover{background:rgba(var(--ion-color-light-rgb),.3)}@media(min-width:768px){.modal-inner-content{width:80%;max-width:700px;border-radius:20px}.header{padding:20px 24px}.dao-header{margin:0 24px 24px}.form{padding:0 24px 24px}}@media(max-width:480px){.modal-inner-content{width:95%;max-height:85vh}.header{padding:14px 16px}.dao-header{margin:0 16px 20px}.form{padding:0 16px 16px}}.dao-header{margin:0 var(--space-xl, 20px) var(--token-space-6, 24px) var(--space-xl, 20px);padding:var(--token-space-4, 16px);background:var(--token-surface-2, var(--surface-flat, rgba(var(--ion-color-light-rgb), .05)));border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);box-shadow:var(--token-elevation-1, var(--shadow-sm));flex-shrink:0;animation:fadeIn var(--token-transition-slow, .3s) ease}.dao-header .step-progress{display:flex;align-items:center;justify-content:center}.dao-header .step-progress .step-indicator{display:flex;align-items:center}.dao-header .step-progress .step-indicator .step-circle{width:36px;height:36px;border-radius:var(--token-radius-full, 50%);display:flex;align-items:center;justify-content:center;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .1));color:var(--ion-text-color);font-weight:var(--token-font-weight-semibold, 600);font-size:var(--token-font-size-2, 14px);border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));transition:all var(--token-transition-slow, .3s) var(--token-ease-in-out, cubic-bezier(.4, 0, .2, 1))}.dao-header .step-progress .step-indicator .step-circle.active{background:var(--ion-color-primary);color:var(--ion-color-primary-contrast);border-color:var(--ion-color-primary);transform:scale(1.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.2)}.dao-header .step-progress .step-indicator .step-circle.completed{background:var(--ion-color-success);color:var(--ion-color-success-contrast);border-color:var(--ion-color-success);transform:scale(1.05)}.dao-header .step-progress .step-indicator .step-circle:hover:not(.active):not(.completed){transform:scale(1.05);background:rgba(var(--ion-color-light-rgb),.15)}.dao-header .step-progress .step-indicator .step-circle ion-icon{font-size:20px}.dao-header .step-progress .step-indicator .step-line{width:40px;height:var(--token-border-width-2, 2px);background:var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));margin:0 var(--token-space-2, 8px);transition:all var(--token-transition-slow, .3s) ease}.dao-header .step-progress .step-indicator .step-line.active{background:var(--ion-color-success);box-shadow:0 0 8px rgba(var(--ion-color-success-rgb),.5)}.membership-section{margin-bottom:var(--token-space-6, 24px);animation:fadeIn var(--token-transition-slow, .3s) ease}.membership-section .explanation{margin-bottom:var(--token-space-4, 16px)}.membership-section .explanation h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-text-color);line-height:var(--token-line-height-tight, 1.3);margin:0 0 var(--token-space-2, 8px) 0}.membership-section .explanation p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);line-height:var(--token-line-height-normal, 1.4);margin:0}.membership-section .options-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--token-space-3, 12px)}.membership-section .options-grid ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-grid ion-card:hover{transform:translateY(-2px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-grid ion-card:active{transform:translateY(0)}.membership-section .options-grid ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-grid ion-card ion-card-content{text-align:center;padding:var(--token-space-5, 20px)}.membership-section .options-grid ion-card ion-card-content .option-icon{margin-bottom:var(--token-space-3, 12px)}.membership-section .options-grid ion-card ion-card-content .option-icon ion-icon{font-size:40px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-grid ion-card ion-card-content h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 6px) 0;color:var(--ion-text-color)}.membership-section .options-grid ion-card ion-card-content p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0 0 var(--token-space-2, 8px) 0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-grid ion-card:hover .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section .options-list{display:flex;flex-direction:column;gap:var(--token-space-3, 12px)}.membership-section .options-list ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-list ion-card:hover{transform:translate(4px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-list ion-card:active{transform:translate(2px)}.membership-section .options-list ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-list ion-card ion-card-content{padding:var(--token-space-4, 16px)}.membership-section .options-list ion-card ion-card-content .protocol-header{display:flex;align-items:center;gap:var(--token-space-3, 12px);margin-bottom:var(--token-space-3, 12px)}.membership-section .options-list ion-card ion-card-content .protocol-header .option-icon ion-icon{font-size:36px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info{flex:1;min-width:0}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-1, 4px) 0;color:var(--ion-text-color)}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-list ion-card ion-card-content .feature-list{list-style:none;padding:0;margin:0}.membership-section .options-list ion-card ion-card-content .feature-list li{display:flex;align-items:center;gap:var(--token-space-2, 8px);padding:var(--token-space-1, 4px) 0;font-size:var(--token-font-size-1, 13px);color:var(--ion-text-color);line-height:var(--token-line-height-normal, 1.4)}.membership-section .options-list ion-card ion-card-content .feature-list li ion-icon{font-size:var(--token-space-4, 16px);flex-shrink:0;color:var(--ion-color-success)}.membership-section .options-list ion-card:hover .protocol-header .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section.connection-progress{display:flex;justify-content:center;align-items:center;min-height:200px}.membership-section.connection-progress .status-info{text-align:center;padding:var(--token-space-6, 24px)}.membership-section.connection-progress .status-info ion-spinner{font-size:48px;margin-bottom:var(--token-space-4, 16px);color:var(--ion-color-primary)}.membership-section.connection-progress .status-info h3{font-size:var(--token-font-size-4, 18px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 8px) 0;color:var(--ion-text-color)}.membership-section.connection-progress .status-info p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);margin:0;max-width:300px;line-height:var(--token-line-height-normal, 1.4)}.membership-section .error-section{padding:var(--token-space-3, 12px);background:rgba(var(--ion-color-danger-rgb),.1);border:1px solid rgba(var(--ion-color-danger-rgb),.2);border-radius:var(--token-radius-2, 8px)}.membership-section .error-section ion-item{--background: transparent;--padding-start: 0;--inner-padding-end: 0}.membership-section .error-section ion-item ion-icon{font-size:var(--token-space-6, 24px)}.membership-section .error-section ion-item ion-label h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-color-danger);margin:0 0 var(--token-space-1, 4px) 0}.membership-section .error-section ion-item ion-label p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.step-content{animation:stepFadeIn var(--token-transition-normal, .25s) var(--token-ease-out, cubic-bezier(0, 0, .2, 1))}.direct-select ion-card{position:relative;overflow:hidden}.direct-select ion-card:after{content:\"\";position:absolute;right:12px;top:50%;transform:translateY(-50%) translate(0);width:0;height:0;border-left:6px solid var(--ion-color-primary);border-top:5px solid transparent;border-bottom:5px solid transparent;opacity:0;transition:all var(--token-transition-fast, .15s) ease}.direct-select ion-card:hover:after{opacity:.6;transform:translateY(-50%) translate(4px)}.direct-select ion-card:hover{border-color:rgba(var(--ion-color-primary-rgb),.4)}.direct-select ion-card:hover .option-icon ion-icon{color:var(--ion-color-primary)}.direct-select ion-card:active{transform:scale(.98);background:rgba(var(--ion-color-primary-rgb),.15)!important}.direct-select.options-grid ion-card:after{right:8px;top:16px;transform:translateY(0) translate(0)}.direct-select.options-grid ion-card:hover:after{transform:translateY(0) translate(4px)}@keyframes fadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),.4)}70%{box-shadow:0 0 0 8px rgba(var(--ion-color-primary-rgb),0)}to{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),0)}}.step-progress .step-indicator .step-circle.active{animation:stepPulse 1.5s ease-out infinite}@media(max-width:768px){.membership-section .options-grid{grid-template-columns:1fr}.dao-header .step-progress .step-indicator .step-circle{width:28px;height:28px;font-size:12px}.dao-header .step-progress .step-indicator .step-line{width:24px}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}@keyframes fadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%,to{box-shadow:none}}}\n"] }]
|
|
10067
|
+
args: [{ selector: 'hsuite-wallet-connection-modal', standalone: true, imports: [CommonModule, IonicModule, ConnectionMethodStepComponent, QrPairingStepComponent], template: "<ion-content>\n <div class=\"modal-inner-content wallet-connection-modal\">\n <div class=\"header\">\n <div class=\"title\">\n <ion-button *ngIf=\"canGoBack()\" (click)=\"back()\" fill=\"clear\" class=\"back-btn\">\n <ion-icon slot=\"icon-only\" name=\"arrow-back-outline\"></ion-icon>\n </ion-button>\n <p>{{ stepTitle() }}</p>\n </div>\n <ion-button class=\"close-modal\" (click)=\"dismiss()\" fill=\"clear\" [disabled]=\"isConnecting()\">\n <ion-icon slot=\"icon-only\" name=\"close-outline\"></ion-icon>\n </ion-button>\n </div>\n\n <!-- Dynamic Step Progress Indicator -->\n <div class=\"dao-header\">\n <div class=\"step-progress\">\n <!-- Dynamic steps based on selected protocol path -->\n <ng-container *ngFor=\"let label of stepConfig().labels; let i = index\">\n <div class=\"step-indicator\">\n <div\n class=\"step-circle\"\n [class.active]=\"displayStep() === i + 1\"\n [class.completed]=\"displayStep() > i + 1\"\n [attr.title]=\"label\"\n >\n <ion-icon *ngIf=\"displayStep() > i + 1\" name=\"checkmark\"></ion-icon>\n <span *ngIf=\"displayStep() <= i + 1\">{{ i + 1 }}</span>\n </div>\n <!-- Step line between circles (not after the last one) -->\n <div\n class=\"step-line\"\n *ngIf=\"i < stepConfig().labels.length - 1\"\n [class.active]=\"displayStep() > i + 1\"\n ></div>\n </div>\n </ng-container>\n </div>\n </div>\n\n <div class=\"form\">\n <!-- Step 1: Network Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.NetworkSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Network</h3>\n <p>Select the network you want to connect to.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let network of networkOptions\"\n (click)=\"selectNetworkAndProceed(network.id)\"\n [class.selected]=\"selectedNetwork() === network.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"network.icon\"></ion-icon>\n </div>\n <h3>{{ network.name }}</h3>\n <p>{{ network.description }}</p>\n <ion-badge *ngIf=\"network.recommended\" color=\"success\">Recommended</ion-badge>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 2: Protocol Selection (Direct Selection - click advances to next step) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ProtocolSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Connection Protocol</h3>\n <p>Choose how you want to connect your wallet.</p>\n </div>\n\n <div class=\"options-list direct-select\">\n <ion-card\n *ngFor=\"let protocol of protocolOptions()\"\n (click)=\"selectProtocolAndProceed(protocol.id)\"\n [class.selected]=\"selectedProtocol() === protocol.id\"\n button\n >\n <ion-card-content>\n <div class=\"protocol-header\">\n <div class=\"option-icon\">\n <ion-icon [name]=\"protocol.icon\"></ion-icon>\n </div>\n <div class=\"protocol-info\">\n <h3>{{ protocol.name }}</h3>\n <p>{{ protocol.description }}</p>\n </div>\n </div>\n\n <ul class=\"feature-list\">\n <li *ngFor=\"let feature of protocol.features\">\n <ion-icon name=\"checkmark-circle\" color=\"success\"></ion-icon>\n <span>{{ feature }}</span>\n </li>\n </ul>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 3: Connection Method Selection (HSuite Native only) -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionMethodSelection\" class=\"step-content\">\n <hsuite-connection-method-step\n [methods]=\"connectionMethodOptions()\"\n [isMobile]=\"isMobile()\"\n [selectedMethod]=\"selectedConnectionMethod()\"\n (methodSelected)=\"onConnectionMethodSelected($event)\"\n ></hsuite-connection-method-step>\n </div>\n\n <!-- Step 3 (WalletConnect path): Ledger Selection (Direct Selection - click initiates connection) -->\n <div *ngIf=\"currentStep() === ConnectionStep.LedgerSelection\" class=\"step-content\">\n <div class=\"membership-section\">\n <div class=\"explanation\">\n <h3>Choose Blockchain</h3>\n <p>Select the blockchain ledger to connect with.</p>\n </div>\n\n <div class=\"options-grid direct-select\">\n <ion-card\n *ngFor=\"let ledger of ledgerOptions\"\n (click)=\"selectLedgerAndConnect(ledger.id)\"\n [class.selected]=\"selectedLedger() === ledger.id\"\n button\n >\n <ion-card-content>\n <div class=\"option-icon\">\n <ion-icon [name]=\"ledger.icon\"></ion-icon>\n </div>\n <h3>{{ ledger.name }}</h3>\n <p>{{ ledger.description }}</p>\n </ion-card-content>\n </ion-card>\n </div>\n </div>\n </div>\n\n <!-- Step 5: QR Pairing (Mobile P2P - Nostr-first, single QR) -->\n <div *ngIf=\"currentStep() === ConnectionStep.QrPairing\" class=\"step-content\">\n <hsuite-qr-pairing-step\n [offerData]=\"pairingOffer()\"\n [appName]=\"appName\"\n [isLoading]=\"isGeneratingOffer()\"\n [isWaitingForWallet]=\"isWaitingForWallet()\"\n [isWaitingForApproval]=\"isWaitingForApproval()\"\n [errorMessage]=\"connectionError()\"\n (expired)=\"onQrExpired()\"\n (refreshRequested)=\"onQrRefresh()\"\n (cancelled)=\"back()\"\n ></hsuite-qr-pairing-step>\n </div>\n\n <!-- Step 6: Connection Progress -->\n <div *ngIf=\"currentStep() === ConnectionStep.ConnectionProgress\" class=\"step-content\">\n <div class=\"membership-section connection-progress\">\n <div *ngIf=\"isConnecting()\" class=\"status-info\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n <h3>Connecting...</h3>\n <p *ngIf=\"selectedProtocol() === 'walletconnect'\">\n Please approve the connection in your wallet app or scan the QR code.\n </p>\n <p *ngIf=\"selectedProtocol() === 'hsuite-native'\">Opening HSuite Wallet...</p>\n </div>\n\n <div *ngIf=\"connectionError()\" class=\"membership-section\">\n <div class=\"error-section\">\n <ion-item lines=\"none\">\n <ion-icon slot=\"start\" name=\"alert-circle-outline\" color=\"danger\"></ion-icon>\n <ion-label>\n <h3>Connection Failed</h3>\n <p>{{ connectionError() }}</p>\n </ion-label>\n </ion-item>\n </div>\n <ion-button expand=\"block\" fill=\"outline\" (click)=\"back()\" class=\"ion-margin-top\">\n Try Again\n </ion-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer removed: All steps now use direct selection (click to proceed) -->\n </div>\n</ion-content>\n", styles: [":host{display:flex;align-items:center;justify-content:center;height:100%;width:100%}ion-content{--background: transparent;--padding-start: 0;--padding-end: 0;--padding-top: 0;--padding-bottom: 0;height:100vh;width:100%;display:flex!important;align-items:center!important;justify-content:center!important}ion-content::part(scroll){display:flex;align-items:center;justify-content:center;min-height:100%;width:100%}.modal-inner-content{width:90%;max-width:600px;max-height:80vh;background:var(--glass-background, rgba(var(--ion-color-dark-rgb), .95));border-radius:var(--border-radius-xl, 16px);padding:0;display:flex;flex-direction:column;box-shadow:var(--shadow-2xl);border:1px solid var(--border-flat, rgba(var(--ion-color-light-rgb), .1));overflow:hidden;animation:modalFadeIn .3s ease}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-lg, 16px) var(--space-xl, 20px);background:transparent;flex-shrink:0}.header .title{display:flex;align-items:center;gap:var(--space-sm, 8px);flex:1}.header .title p{margin:0;font-size:var(--font-size-2xl, 24px);font-weight:var(--font-weight-bold, 700);color:var(--ion-text-color);line-height:1.2}.header .back-btn{--padding-start: 8px;--padding-end: 8px;margin:0}.header .back-btn ion-icon{font-size:22px;color:var(--ion-color-primary)}.header .close-modal{--padding-start: 8px;--padding-end: 8px;margin:0;cursor:pointer}.header .close-modal ion-icon{font-size:24px;color:var(--ion-text-color)}.header .close-modal:hover:not(:disabled){transform:scale(1.05)}.header .close-modal:active:not(:disabled){transform:scale(.95)}.form{flex:1;overflow-y:auto;padding:0 var(--space-xl, 20px) var(--space-xl, 20px) var(--space-xl, 20px);background:transparent}.form::-webkit-scrollbar{width:8px}.form::-webkit-scrollbar-track{background:var(--surface-flat, rgba(var(--ion-color-light-rgb), .05));border-radius:4px}.form::-webkit-scrollbar-thumb{background:rgba(var(--ion-color-light-rgb),.2);border-radius:4px}.form::-webkit-scrollbar-thumb:hover{background:rgba(var(--ion-color-light-rgb),.3)}@media(min-width:768px){.modal-inner-content{width:80%;max-width:700px;border-radius:20px}.header{padding:20px 24px}.dao-header{margin:0 24px 24px}.form{padding:0 24px 24px}}@media(max-width:480px){.modal-inner-content{width:95%;max-height:85vh}.header{padding:14px 16px}.dao-header{margin:0 16px 20px}.form{padding:0 16px 16px}}.dao-header{margin:0 var(--space-xl, 20px) var(--token-space-6, 24px) var(--space-xl, 20px);padding:var(--token-space-4, 16px);background:var(--token-surface-2, var(--surface-flat, rgba(var(--ion-color-light-rgb), .05)));border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);box-shadow:var(--token-elevation-1, var(--shadow-sm));flex-shrink:0;animation:fadeIn var(--token-transition-slow, .3s) ease}.dao-header .step-progress{display:flex;align-items:center;justify-content:center}.dao-header .step-progress .step-indicator{display:flex;align-items:center}.dao-header .step-progress .step-indicator .step-circle{width:36px;height:36px;border-radius:var(--token-radius-full, 50%);display:flex;align-items:center;justify-content:center;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .1));color:var(--ion-text-color);font-weight:var(--token-font-weight-semibold, 600);font-size:var(--token-font-size-2, 14px);border:var(--token-border-width-1, 1px) solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));transition:all var(--token-transition-slow, .3s) var(--token-ease-in-out, cubic-bezier(.4, 0, .2, 1))}.dao-header .step-progress .step-indicator .step-circle.active{background:var(--ion-color-primary);color:var(--ion-color-primary-contrast);border-color:var(--ion-color-primary);transform:scale(1.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.2)}.dao-header .step-progress .step-indicator .step-circle.completed{background:var(--ion-color-success);color:var(--ion-color-success-contrast);border-color:var(--ion-color-success);transform:scale(1.05)}.dao-header .step-progress .step-indicator .step-circle:hover:not(.active):not(.completed){transform:scale(1.05);background:rgba(var(--ion-color-light-rgb),.15)}.dao-header .step-progress .step-indicator .step-circle ion-icon{font-size:20px}.dao-header .step-progress .step-indicator .step-line{width:40px;height:var(--token-border-width-2, 2px);background:var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .2)));margin:0 var(--token-space-2, 8px);transition:all var(--token-transition-slow, .3s) ease}.dao-header .step-progress .step-indicator .step-line.active{background:var(--ion-color-success);box-shadow:0 0 8px rgba(var(--ion-color-success-rgb),.5)}.membership-section{margin-bottom:var(--token-space-6, 24px);animation:fadeIn var(--token-transition-slow, .3s) ease}.membership-section .explanation{margin-bottom:var(--token-space-4, 16px)}.membership-section .explanation h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-text-color);line-height:var(--token-line-height-tight, 1.3);margin:0 0 var(--token-space-2, 8px) 0}.membership-section .explanation p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);line-height:var(--token-line-height-normal, 1.4);margin:0}.membership-section .options-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--token-space-3, 12px)}.membership-section .options-grid ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-grid ion-card:hover{transform:translateY(-2px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-grid ion-card:active{transform:translateY(0)}.membership-section .options-grid ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-grid ion-card ion-card-content{text-align:center;padding:var(--token-space-5, 20px)}.membership-section .options-grid ion-card ion-card-content .option-icon{margin-bottom:var(--token-space-3, 12px)}.membership-section .options-grid ion-card ion-card-content .option-icon ion-icon{font-size:40px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-grid ion-card ion-card-content h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 6px) 0;color:var(--ion-text-color)}.membership-section .options-grid ion-card ion-card-content p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0 0 var(--token-space-2, 8px) 0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-grid ion-card:hover .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section .options-list{display:flex;flex-direction:column;gap:var(--token-space-3, 12px)}.membership-section .options-list ion-card{margin:0;background:var(--token-surface-3, rgba(var(--ion-color-light-rgb), .05));border:1px solid var(--border-flat, var(--token-border-subtle, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--token-radius-3, 12px);transition:all var(--token-transition-normal, .2s) ease;cursor:pointer;box-shadow:var(--token-elevation-1, var(--shadow-sm))}.membership-section .options-list ion-card:hover{transform:translate(4px);background:var(--token-surface-hover, rgba(var(--ion-color-light-rgb), .08));border-color:var(--border-flat, rgba(var(--ion-color-light-rgb), .2));box-shadow:var(--token-elevation-2, var(--shadow-md))}.membership-section .options-list ion-card:active{transform:translate(2px)}.membership-section .options-list ion-card.selected{border:2px solid var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.1);box-shadow:0 0 0 4px rgba(var(--ion-color-primary-rgb),.1)}.membership-section .options-list ion-card ion-card-content{padding:var(--token-space-4, 16px)}.membership-section .options-list ion-card ion-card-content .protocol-header{display:flex;align-items:center;gap:var(--token-space-3, 12px);margin-bottom:var(--token-space-3, 12px)}.membership-section .options-list ion-card ion-card-content .protocol-header .option-icon ion-icon{font-size:36px;color:var(--ion-color-primary);transition:transform var(--token-transition-fast, .15s) ease}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info{flex:1;min-width:0}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-1, 4px) 0;color:var(--ion-text-color)}.membership-section .options-list ion-card ion-card-content .protocol-header .protocol-info p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.membership-section .options-list ion-card ion-card-content .feature-list{list-style:none;padding:0;margin:0}.membership-section .options-list ion-card ion-card-content .feature-list li{display:flex;align-items:center;gap:var(--token-space-2, 8px);padding:var(--token-space-1, 4px) 0;font-size:var(--token-font-size-1, 13px);color:var(--ion-text-color);line-height:var(--token-line-height-normal, 1.4)}.membership-section .options-list ion-card ion-card-content .feature-list li ion-icon{font-size:var(--token-space-4, 16px);flex-shrink:0;color:var(--ion-color-success)}.membership-section .options-list ion-card:hover .protocol-header .option-icon ion-icon{transform:scale(1.1) rotate(5deg)}.membership-section.connection-progress{display:flex;justify-content:center;align-items:center;min-height:200px}.membership-section.connection-progress .status-info{text-align:center;padding:var(--token-space-6, 24px)}.membership-section.connection-progress .status-info ion-spinner{font-size:48px;margin-bottom:var(--token-space-4, 16px);color:var(--ion-color-primary)}.membership-section.connection-progress .status-info h3{font-size:var(--token-font-size-4, 18px);font-weight:var(--token-font-weight-medium, 500);margin:0 0 var(--token-space-2, 8px) 0;color:var(--ion-text-color)}.membership-section.connection-progress .status-info p{font-size:var(--token-font-size-2, 14px);color:var(--ion-color-medium);margin:0;max-width:300px;line-height:var(--token-line-height-normal, 1.4)}.membership-section .error-section{padding:var(--token-space-3, 12px);background:rgba(var(--ion-color-danger-rgb),.1);border:1px solid rgba(var(--ion-color-danger-rgb),.2);border-radius:var(--token-radius-2, 8px)}.membership-section .error-section ion-item{--background: transparent;--padding-start: 0;--inner-padding-end: 0}.membership-section .error-section ion-item ion-icon{font-size:var(--token-space-6, 24px)}.membership-section .error-section ion-item ion-label h3{font-size:var(--token-font-size-3, 16px);font-weight:var(--token-font-weight-medium, 500);color:var(--ion-color-danger);margin:0 0 var(--token-space-1, 4px) 0}.membership-section .error-section ion-item ion-label p{font-size:var(--token-font-size-1, 13px);color:var(--ion-color-medium);margin:0;line-height:var(--token-line-height-tight, 1.3)}.step-content{animation:stepFadeIn var(--token-transition-normal, .25s) var(--token-ease-out, cubic-bezier(0, 0, .2, 1))}.direct-select ion-card{position:relative;overflow:hidden}.direct-select ion-card:after{content:\"\";position:absolute;right:12px;top:50%;transform:translateY(-50%) translate(0);width:0;height:0;border-left:6px solid var(--ion-color-primary);border-top:5px solid transparent;border-bottom:5px solid transparent;opacity:0;transition:all var(--token-transition-fast, .15s) ease}.direct-select ion-card:hover:after{opacity:.6;transform:translateY(-50%) translate(4px)}.direct-select ion-card:hover{border-color:rgba(var(--ion-color-primary-rgb),.4)}.direct-select ion-card:hover .option-icon ion-icon{color:var(--ion-color-primary)}.direct-select ion-card:active{transform:scale(.98);background:rgba(var(--ion-color-primary-rgb),.15)!important}.direct-select.options-grid ion-card:after{right:8px;top:16px;transform:translateY(0) translate(0)}.direct-select.options-grid ion-card:hover:after{transform:translateY(0) translate(4px)}@keyframes fadeIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),.4)}70%{box-shadow:0 0 0 8px rgba(var(--ion-color-primary-rgb),0)}to{box-shadow:0 0 rgba(var(--ion-color-primary-rgb),0)}}.step-progress .step-indicator .step-circle.active{animation:stepPulse 1.5s ease-out infinite}@media(max-width:768px){.membership-section .options-grid{grid-template-columns:1fr}.dao-header .step-progress .step-indicator .step-circle{width:28px;height:28px;font-size:12px}.dao-header .step-progress .step-indicator .step-line{width:24px}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}@keyframes fadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepFadeIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes stepPulse{0%,to{box-shadow:none}}}\n"] }]
|
|
9877
10068
|
}], ctorParameters: () => [{ type: i1$1.ModalController }, { type: UnifiedWalletService }], propDecorators: { projectId: [{
|
|
9878
10069
|
type: Input
|
|
10070
|
+
}], network: [{
|
|
10071
|
+
type: Input
|
|
9879
10072
|
}], appName: [{
|
|
9880
10073
|
type: Input
|
|
9881
10074
|
}], appDescription: [{
|
|
@@ -9895,7 +10088,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
9895
10088
|
* This file is part of HSuite Native Connect. For commercial licensing,
|
|
9896
10089
|
* visit https://hsuite.finance/licensing
|
|
9897
10090
|
*/
|
|
9898
|
-
const logger$
|
|
10091
|
+
const logger$4 = getLogger().scoped?.('WalletConnectButton') ?? getLogger();
|
|
9899
10092
|
/**
|
|
9900
10093
|
* @component WalletConnectButtonComponent
|
|
9901
10094
|
* Ready-to-use wallet connection button for dApps with multi-protocol support.
|
|
@@ -9932,6 +10125,12 @@ class WalletConnectButtonComponent {
|
|
|
9932
10125
|
* Wallet URL for targeting the wallet window
|
|
9933
10126
|
*/
|
|
9934
10127
|
walletUrl = DEFAULT_WALLET_URL;
|
|
10128
|
+
/**
|
|
10129
|
+
* Pins the connection network and hides the network-selection step in the
|
|
10130
|
+
* modal. When set to `'mainnet'` or `'testnet'`, the dApp decides the network
|
|
10131
|
+
* and the user never sees the picker. When omitted, the user chooses.
|
|
10132
|
+
*/
|
|
10133
|
+
network;
|
|
9935
10134
|
/**
|
|
9936
10135
|
* Whether to show session information when connected
|
|
9937
10136
|
* @default true
|
|
@@ -9994,7 +10193,7 @@ class WalletConnectButtonComponent {
|
|
|
9994
10193
|
catch (err) {
|
|
9995
10194
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
9996
10195
|
this.error.emit(error);
|
|
9997
|
-
logger$
|
|
10196
|
+
logger$4.error('Connection failed', { error: error.message });
|
|
9998
10197
|
}
|
|
9999
10198
|
finally {
|
|
10000
10199
|
this.isConnecting = false;
|
|
@@ -10012,6 +10211,7 @@ class WalletConnectButtonComponent {
|
|
|
10012
10211
|
appName: this.appName,
|
|
10013
10212
|
appDescription: this.appDescription,
|
|
10014
10213
|
walletUrl: this.walletUrl,
|
|
10214
|
+
network: this.network,
|
|
10015
10215
|
},
|
|
10016
10216
|
});
|
|
10017
10217
|
await modal.present();
|
|
@@ -10039,7 +10239,7 @@ class WalletConnectButtonComponent {
|
|
|
10039
10239
|
catch (err) {
|
|
10040
10240
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
10041
10241
|
this.error.emit(error);
|
|
10042
|
-
logger$
|
|
10242
|
+
logger$4.error('Disconnect failed', { error: error.message });
|
|
10043
10243
|
}
|
|
10044
10244
|
}
|
|
10045
10245
|
/**
|
|
@@ -10083,7 +10283,7 @@ class WalletConnectButtonComponent {
|
|
|
10083
10283
|
return this.wallet.allAccounts();
|
|
10084
10284
|
}
|
|
10085
10285
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletConnectButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10086
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: WalletConnectButtonComponent, isStandalone: true, selector: "wallet-connect-button", inputs: { walletConnectProjectId: "walletConnectProjectId", appName: "appName", appDescription: "appDescription", walletUrl: "walletUrl", showSessionInfo: "showSessionInfo", buttonColor: "buttonColor", size: "size", connectText: "connectText", connectedText: "connectedText", multiSessionMode: "multiSessionMode" }, outputs: { connected: "connected", disconnected: "disconnected", error: "error" }, providers: [ModalController], ngImport: i0, template: "<div class=\"wallet-connect-container\" [class]=\"'size-' + size\">\n <!-- Connect Button (Multi-Session Support) -->\n <ion-button\n [color]=\"buttonColor\"\n [size]=\"size\"\n [disabled]=\"isConnecting\"\n (click)=\"connect()\"\n class=\"wallet-button\">\n <ion-spinner *ngIf=\"isConnecting\" slot=\"start\" name=\"crescent\"></ion-spinner>\n <ion-icon *ngIf=\"!isConnecting\" [name]=\"getIconName()\" slot=\"start\"></ion-icon>\n {{ getButtonText() }}\n </ion-button>\n</div>\n\n", styles: [".wallet-connect-container{display:inline-block;width:100%}.wallet-button{--border-radius: var(--wallet-button-radius, var(--border-radius-lg, 12px));--padding-top: var(--wallet-button-padding-vertical, var(--space-md, 14px));--padding-bottom: var(--wallet-button-padding-vertical, var(--space-md, 14px));--padding-start: var(--wallet-button-padding-horizontal, var(--space-lg, 24px));--padding-end: var(--wallet-button-padding-horizontal, var(--space-lg, 24px));width:100%;font-weight:var(--wallet-button-font-weight, var(--font-weight-semibold, 600));font-size:var(--wallet-button-font-size, var(--font-size-md, 16px));text-transform:none;letter-spacing:var(--letter-spacing-wide, .5px);transition:var(--wallet-transition-normal, var(--transition-all, .2s ease));box-shadow:var(--wallet-button-shadow, var(--shadow-md))}.wallet-button:hover:not([disabled]){transform:translateY(-2px);box-shadow:var(--shadow-lg)}.wallet-button:active:not([disabled]){transform:translateY(0)}.wallet-button ion-icon{font-size:1.25em;transition:transform var(--wallet-transition-fast, var(--transition-fade, .15s)) ease}.wallet-button:hover:not([disabled]) ion-icon{transform:scale(var(--button-hover-scale, 1.1))}.session-display{background:var(--wallet-session-bg, var(--surface-flat, rgba(var(--ion-color-light-rgb), .05)));border:var(--border-width-thin, 1px) solid var(--wallet-session-border, var(--border-flat, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--wallet-session-radius, var(--border-radius-lg, 12px));padding:var(--wallet-session-padding, var(--space-lg, 16px));display:flex;flex-direction:column;gap:var(--wallet-session-gap, var(--space-md, 12px));box-shadow:var(--wallet-session-shadow, var(--shadow-md));animation:slideIn var(--wallet-transition-slow, var(--duration-slow, .3s)) ease-out}.session-header{display:flex;align-items:center;justify-content:space-between;gap:var(--wallet-spacing-lg, var(--space-lg, 16px))}.session-info{display:flex;align-items:center;gap:var(--wallet-spacing-md, var(--space-md, 8px));flex:1;min-width:0}.session-icon{font-size:var(--wallet-session-icon-size, var(--font-size-2xl, 28px));color:var(--ion-color-primary);flex-shrink:0;transition:transform var(--wallet-transition-fast, var(--duration-fast, .15s)) ease}.session-display:hover .session-icon{transform:scale(1.05)}.session-details{display:flex;flex-direction:column;gap:var(--wallet-spacing-xs, var(--space-xs, 4px));min-width:0;flex:1}.session-details strong{font-size:var(--wallet-font-size-base, var(--font-size-sm, 14px));font-weight:var(--wallet-font-weight-semibold, var(--font-weight-semibold, 600));color:var(--wallet-session-text-primary, var(--ion-text-color));line-height:var(--wallet-line-height-normal, var(--line-height-normal, 1.4));display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-badges{display:flex;gap:var(--wallet-spacing-xs, var(--space-xs, 4px));flex-wrap:wrap}.network-badge{font-size:var(--wallet-badge-font-size, var(--font-size-xs, 10px));font-weight:var(--wallet-badge-font-weight, var(--font-weight-semibold, 600));padding:var(--wallet-badge-padding, var(--space-xs, 3px) var(--space-sm, 8px));border-radius:var(--wallet-badge-radius, var(--border-radius-sm, 6px));line-height:var(--line-height-tight, 1.2);transition:transform var(--wallet-transition-fast, var(--duration-fast, .15s)) ease}.session-display:hover .network-badge{transform:scale(1.05)}.disconnect-button{--border-radius: var(--wallet-spacing-sm, var(--border-radius-md, 8px));--padding-start: var(--wallet-spacing-sm, var(--space-sm, 8px));--padding-end: var(--wallet-spacing-sm, var(--space-sm, 8px));flex-shrink:0;transition:all var(--wallet-transition-fast, var(--transition-all, .15s ease))}.disconnect-button:hover:not([disabled]){transform:scale(1.05)}.disconnect-button:active:not([disabled]){transform:scale(var(--button-press-scale, .95))}.account-list{display:flex;flex-direction:column;gap:var(--wallet-spacing-sm, var(--space-sm, 4px));padding-top:var(--wallet-spacing-md, var(--space-md, 8px));border-top:var(--border-width-thin, 1px) solid var(--wallet-session-border, var(--border-flat, rgba(var(--ion-color-light-rgb), .1)))}.account-item{display:flex;align-items:center;justify-content:space-between;padding:var(--wallet-account-padding, var(--space-sm, 8px) var(--space-md, 12px));background:var(--wallet-account-bg, var(--surface-flat, rgba(var(--ion-color-primary-rgb), .05)));border-radius:var(--wallet-account-radius, var(--border-radius-md, 8px));transition:all var(--wallet-transition-normal, var(--transition-all, .2s ease))}.account-item:hover{background:var(--wallet-account-bg-hover, var(--surface-flat-hover, rgba(var(--ion-color-primary-rgb), .1)));transform:translate(2px)}.account-item .account-address{font-family:var(--wallet-account-font-family, var(--font-family-mono, monospace));font-size:var(--wallet-account-font-size, var(--font-size-sm, 14px));color:var(--wallet-session-text-primary, var(--ion-text-color));line-height:var(--wallet-line-height-normal, var(--line-height-normal, 1.4));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.size-small{--wallet-session-padding: var(--wallet-spacing-md, var(--space-md, 12px))}.size-small .wallet-button{--padding-top: var(--wallet-spacing-sm, var(--space-sm, 8px));--padding-bottom: var(--wallet-spacing-sm, var(--space-sm, 8px));--padding-start: var(--wallet-spacing-lg, var(--space-lg, 16px));--padding-end: var(--wallet-spacing-lg, var(--space-lg, 16px));font-size:var(--wallet-font-size-sm, var(--font-size-sm, 13px))}.size-small .session-icon{font-size:var(--font-size-xl, 24px)}.size-small .session-details strong{font-size:var(--wallet-font-size-sm, var(--font-size-sm, 13px))}.size-large{--wallet-session-padding: var(--wallet-spacing-xl, var(--space-xl, 20px))}.size-large .wallet-button{--padding-top: var(--wallet-spacing-lg, var(--space-lg, 18px));--padding-bottom: var(--wallet-spacing-lg, var(--space-lg, 18px));--padding-start: var(--wallet-spacing-2xl, var(--space-2xl, 32px));--padding-end: var(--wallet-spacing-2xl, var(--space-2xl, 32px));font-size:var(--wallet-font-size-lg, var(--font-size-lg, 18px))}.size-large .session-icon{font-size:var(--font-size-3xl, 32px)}.size-large .session-details strong{font-size:var(--wallet-font-size-lg, var(--font-size-lg, 18px))}@media(max-width:576px){.session-header{flex-wrap:wrap}.disconnect-button{width:100%}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.6}}.wallet-button[disabled]{animation:pulse 1.5s ease-in-out infinite}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}@keyframes slideIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes pulse{0%,to{opacity:1}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] });
|
|
10286
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: WalletConnectButtonComponent, isStandalone: true, selector: "wallet-connect-button", inputs: { walletConnectProjectId: "walletConnectProjectId", appName: "appName", appDescription: "appDescription", walletUrl: "walletUrl", network: "network", showSessionInfo: "showSessionInfo", buttonColor: "buttonColor", size: "size", connectText: "connectText", connectedText: "connectedText", multiSessionMode: "multiSessionMode" }, outputs: { connected: "connected", disconnected: "disconnected", error: "error" }, providers: [ModalController], ngImport: i0, template: "<div class=\"wallet-connect-container\" [class]=\"'size-' + size\">\n <!-- Connect Button (Multi-Session Support) -->\n <ion-button\n [color]=\"buttonColor\"\n [size]=\"size\"\n [disabled]=\"isConnecting\"\n (click)=\"connect()\"\n class=\"wallet-button\">\n <ion-spinner *ngIf=\"isConnecting\" slot=\"start\" name=\"crescent\"></ion-spinner>\n <ion-icon *ngIf=\"!isConnecting\" [name]=\"getIconName()\" slot=\"start\"></ion-icon>\n {{ getButtonText() }}\n </ion-button>\n</div>\n\n", styles: [".wallet-connect-container{display:inline-block;width:100%}.wallet-button{--border-radius: var(--wallet-button-radius, var(--border-radius-lg, 12px));--padding-top: var(--wallet-button-padding-vertical, var(--space-md, 14px));--padding-bottom: var(--wallet-button-padding-vertical, var(--space-md, 14px));--padding-start: var(--wallet-button-padding-horizontal, var(--space-lg, 24px));--padding-end: var(--wallet-button-padding-horizontal, var(--space-lg, 24px));width:100%;font-weight:var(--wallet-button-font-weight, var(--font-weight-semibold, 600));font-size:var(--wallet-button-font-size, var(--font-size-md, 16px));text-transform:none;letter-spacing:var(--letter-spacing-wide, .5px);transition:var(--wallet-transition-normal, var(--transition-all, .2s ease));box-shadow:var(--wallet-button-shadow, var(--shadow-md))}.wallet-button:hover:not([disabled]){transform:translateY(-2px);box-shadow:var(--shadow-lg)}.wallet-button:active:not([disabled]){transform:translateY(0)}.wallet-button ion-icon{font-size:1.25em;transition:transform var(--wallet-transition-fast, var(--transition-fade, .15s)) ease}.wallet-button:hover:not([disabled]) ion-icon{transform:scale(var(--button-hover-scale, 1.1))}.session-display{background:var(--wallet-session-bg, var(--surface-flat, rgba(var(--ion-color-light-rgb), .05)));border:var(--border-width-thin, 1px) solid var(--wallet-session-border, var(--border-flat, rgba(var(--ion-color-light-rgb), .1)));border-radius:var(--wallet-session-radius, var(--border-radius-lg, 12px));padding:var(--wallet-session-padding, var(--space-lg, 16px));display:flex;flex-direction:column;gap:var(--wallet-session-gap, var(--space-md, 12px));box-shadow:var(--wallet-session-shadow, var(--shadow-md));animation:slideIn var(--wallet-transition-slow, var(--duration-slow, .3s)) ease-out}.session-header{display:flex;align-items:center;justify-content:space-between;gap:var(--wallet-spacing-lg, var(--space-lg, 16px))}.session-info{display:flex;align-items:center;gap:var(--wallet-spacing-md, var(--space-md, 8px));flex:1;min-width:0}.session-icon{font-size:var(--wallet-session-icon-size, var(--font-size-2xl, 28px));color:var(--ion-color-primary);flex-shrink:0;transition:transform var(--wallet-transition-fast, var(--duration-fast, .15s)) ease}.session-display:hover .session-icon{transform:scale(1.05)}.session-details{display:flex;flex-direction:column;gap:var(--wallet-spacing-xs, var(--space-xs, 4px));min-width:0;flex:1}.session-details strong{font-size:var(--wallet-font-size-base, var(--font-size-sm, 14px));font-weight:var(--wallet-font-weight-semibold, var(--font-weight-semibold, 600));color:var(--wallet-session-text-primary, var(--ion-text-color));line-height:var(--wallet-line-height-normal, var(--line-height-normal, 1.4));display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-badges{display:flex;gap:var(--wallet-spacing-xs, var(--space-xs, 4px));flex-wrap:wrap}.network-badge{font-size:var(--wallet-badge-font-size, var(--font-size-xs, 10px));font-weight:var(--wallet-badge-font-weight, var(--font-weight-semibold, 600));padding:var(--wallet-badge-padding, var(--space-xs, 3px) var(--space-sm, 8px));border-radius:var(--wallet-badge-radius, var(--border-radius-sm, 6px));line-height:var(--line-height-tight, 1.2);transition:transform var(--wallet-transition-fast, var(--duration-fast, .15s)) ease}.session-display:hover .network-badge{transform:scale(1.05)}.disconnect-button{--border-radius: var(--wallet-spacing-sm, var(--border-radius-md, 8px));--padding-start: var(--wallet-spacing-sm, var(--space-sm, 8px));--padding-end: var(--wallet-spacing-sm, var(--space-sm, 8px));flex-shrink:0;transition:all var(--wallet-transition-fast, var(--transition-all, .15s ease))}.disconnect-button:hover:not([disabled]){transform:scale(1.05)}.disconnect-button:active:not([disabled]){transform:scale(var(--button-press-scale, .95))}.account-list{display:flex;flex-direction:column;gap:var(--wallet-spacing-sm, var(--space-sm, 4px));padding-top:var(--wallet-spacing-md, var(--space-md, 8px));border-top:var(--border-width-thin, 1px) solid var(--wallet-session-border, var(--border-flat, rgba(var(--ion-color-light-rgb), .1)))}.account-item{display:flex;align-items:center;justify-content:space-between;padding:var(--wallet-account-padding, var(--space-sm, 8px) var(--space-md, 12px));background:var(--wallet-account-bg, var(--surface-flat, rgba(var(--ion-color-primary-rgb), .05)));border-radius:var(--wallet-account-radius, var(--border-radius-md, 8px));transition:all var(--wallet-transition-normal, var(--transition-all, .2s ease))}.account-item:hover{background:var(--wallet-account-bg-hover, var(--surface-flat-hover, rgba(var(--ion-color-primary-rgb), .1)));transform:translate(2px)}.account-item .account-address{font-family:var(--wallet-account-font-family, var(--font-family-mono, monospace));font-size:var(--wallet-account-font-size, var(--font-size-sm, 14px));color:var(--wallet-session-text-primary, var(--ion-text-color));line-height:var(--wallet-line-height-normal, var(--line-height-normal, 1.4));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.size-small{--wallet-session-padding: var(--wallet-spacing-md, var(--space-md, 12px))}.size-small .wallet-button{--padding-top: var(--wallet-spacing-sm, var(--space-sm, 8px));--padding-bottom: var(--wallet-spacing-sm, var(--space-sm, 8px));--padding-start: var(--wallet-spacing-lg, var(--space-lg, 16px));--padding-end: var(--wallet-spacing-lg, var(--space-lg, 16px));font-size:var(--wallet-font-size-sm, var(--font-size-sm, 13px))}.size-small .session-icon{font-size:var(--font-size-xl, 24px)}.size-small .session-details strong{font-size:var(--wallet-font-size-sm, var(--font-size-sm, 13px))}.size-large{--wallet-session-padding: var(--wallet-spacing-xl, var(--space-xl, 20px))}.size-large .wallet-button{--padding-top: var(--wallet-spacing-lg, var(--space-lg, 18px));--padding-bottom: var(--wallet-spacing-lg, var(--space-lg, 18px));--padding-start: var(--wallet-spacing-2xl, var(--space-2xl, 32px));--padding-end: var(--wallet-spacing-2xl, var(--space-2xl, 32px));font-size:var(--wallet-font-size-lg, var(--font-size-lg, 18px))}.size-large .session-icon{font-size:var(--font-size-3xl, 32px)}.size-large .session-details strong{font-size:var(--wallet-font-size-lg, var(--font-size-lg, 18px))}@media(max-width:576px){.session-header{flex-wrap:wrap}.disconnect-button{width:100%}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.6}}.wallet-button[disabled]{animation:pulse 1.5s ease-in-out infinite}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}@keyframes slideIn{0%,to{opacity:1;transform:translateY(0)}}@keyframes pulse{0%,to{opacity:1}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }] });
|
|
10087
10287
|
}
|
|
10088
10288
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletConnectButtonComponent, decorators: [{
|
|
10089
10289
|
type: Component,
|
|
@@ -10096,6 +10296,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
10096
10296
|
type: Input
|
|
10097
10297
|
}], walletUrl: [{
|
|
10098
10298
|
type: Input
|
|
10299
|
+
}], network: [{
|
|
10300
|
+
type: Input
|
|
10099
10301
|
}], showSessionInfo: [{
|
|
10100
10302
|
type: Input
|
|
10101
10303
|
}], buttonColor: [{
|
|
@@ -10183,7 +10385,7 @@ class WalletConnectPromptComponent {
|
|
|
10183
10385
|
addIcons({ walletOutline });
|
|
10184
10386
|
}
|
|
10185
10387
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletConnectPromptComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10186
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: WalletConnectPromptComponent, isStandalone: true, selector: "wallet-connect-prompt", inputs: { title: "title", subtitle: "subtitle", message: "message", showIcon: "showIcon", buttonText: "buttonText", buttonSize: "buttonSize", buttonColor: "buttonColor", cardColor: "cardColor", walletConnectProjectId: "walletConnectProjectId" }, ngImport: i0, template: "<ion-card class=\"wallet-connect-prompt\" [color]=\"cardColor\">\n <ion-card-header>\n <div class=\"prompt-header\">\n <ion-icon *ngIf=\"showIcon\" name=\"wallet-outline\" class=\"prompt-icon\"></ion-icon>\n <div>\n <ion-card-title>{{ title }}</ion-card-title>\n <ion-card-subtitle *ngIf=\"subtitle\">{{ subtitle }}</ion-card-subtitle>\n </div>\n </div>\n </ion-card-header>\n \n <ion-card-content>\n <p class=\"prompt-message\">{{ message }}</p>\n \n <wallet-connect-button\n [walletConnectProjectId]=\"walletConnectProjectId\"\n [connectText]=\"buttonText\"\n [size]=\"buttonSize\"\n [buttonColor]=\"buttonColor\"\n class=\"prompt-button\"\n />\n </ion-card-content>\n</ion-card>\n\n", styles: [".wallet-connect-prompt{max-width:600px;margin:var(--space-lg, 24px) auto;text-align:center}.prompt-header{display:flex;flex-direction:column;align-items:center;gap:var(--space-sm, 12px)}.prompt-icon{font-size:48px;color:var(--ion-color-primary)}.prompt-message{margin:var(--space-md, 16px) 0;color:var(--ion-color-medium);font-size:14px;line-height:1.6}.prompt-button{margin-top:var(--space-md, 16px)}.prompt-button ::ng-deep ion-button{margin:0 auto;display:block}@media(max-width:576px){.wallet-connect-prompt{margin:var(--space-md, 16px)}.prompt-icon{font-size:36px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: WalletConnectButtonComponent, selector: "wallet-connect-button", inputs: ["walletConnectProjectId", "appName", "appDescription", "walletUrl", "showSessionInfo", "buttonColor", "size", "connectText", "connectedText", "multiSessionMode"], outputs: ["connected", "disconnected", "error"] }] });
|
|
10388
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: WalletConnectPromptComponent, isStandalone: true, selector: "wallet-connect-prompt", inputs: { title: "title", subtitle: "subtitle", message: "message", showIcon: "showIcon", buttonText: "buttonText", buttonSize: "buttonSize", buttonColor: "buttonColor", cardColor: "cardColor", walletConnectProjectId: "walletConnectProjectId" }, ngImport: i0, template: "<ion-card class=\"wallet-connect-prompt\" [color]=\"cardColor\">\n <ion-card-header>\n <div class=\"prompt-header\">\n <ion-icon *ngIf=\"showIcon\" name=\"wallet-outline\" class=\"prompt-icon\"></ion-icon>\n <div>\n <ion-card-title>{{ title }}</ion-card-title>\n <ion-card-subtitle *ngIf=\"subtitle\">{{ subtitle }}</ion-card-subtitle>\n </div>\n </div>\n </ion-card-header>\n \n <ion-card-content>\n <p class=\"prompt-message\">{{ message }}</p>\n \n <wallet-connect-button\n [walletConnectProjectId]=\"walletConnectProjectId\"\n [connectText]=\"buttonText\"\n [size]=\"buttonSize\"\n [buttonColor]=\"buttonColor\"\n class=\"prompt-button\"\n />\n </ion-card-content>\n</ion-card>\n\n", styles: [".wallet-connect-prompt{max-width:600px;margin:var(--space-lg, 24px) auto;text-align:center}.prompt-header{display:flex;flex-direction:column;align-items:center;gap:var(--space-sm, 12px)}.prompt-icon{font-size:48px;color:var(--ion-color-primary)}.prompt-message{margin:var(--space-md, 16px) 0;color:var(--ion-color-medium);font-size:14px;line-height:1.6}.prompt-button{margin-top:var(--space-md, 16px)}.prompt-button ::ng-deep ion-button{margin:0 auto;display:block}@media(max-width:576px){.wallet-connect-prompt{margin:var(--space-md, 16px)}.prompt-icon{font-size:36px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: WalletConnectButtonComponent, selector: "wallet-connect-button", inputs: ["walletConnectProjectId", "appName", "appDescription", "walletUrl", "network", "showSessionInfo", "buttonColor", "size", "connectText", "connectedText", "multiSessionMode"], outputs: ["connected", "disconnected", "error"] }] });
|
|
10187
10389
|
}
|
|
10188
10390
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: WalletConnectPromptComponent, decorators: [{
|
|
10189
10391
|
type: Component,
|
|
@@ -11432,7 +11634,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
11432
11634
|
* }
|
|
11433
11635
|
* ```
|
|
11434
11636
|
*/
|
|
11435
|
-
const logger$
|
|
11637
|
+
const logger$3 = getLogger().scoped?.('TransactionService') ?? getLogger();
|
|
11436
11638
|
/**
|
|
11437
11639
|
* High-level transaction service with automatic state management.
|
|
11438
11640
|
*
|
|
@@ -11484,7 +11686,7 @@ class TransactionService {
|
|
|
11484
11686
|
async signTransaction(payload) {
|
|
11485
11687
|
return this.executeTransaction(async () => {
|
|
11486
11688
|
const result = (await this.walletService.signTransaction(payload));
|
|
11487
|
-
logger$
|
|
11689
|
+
logger$3.info('Transaction signed', {
|
|
11488
11690
|
hasSignature: !!result.signedTransaction,
|
|
11489
11691
|
});
|
|
11490
11692
|
await this.showSuccessToast('Transaction Signed', 'Your transaction has been signed successfully.');
|
|
@@ -11505,7 +11707,7 @@ class TransactionService {
|
|
|
11505
11707
|
async submitTransaction(payload) {
|
|
11506
11708
|
return this.executeTransaction(async () => {
|
|
11507
11709
|
const result = (await this.walletService.submitTransaction(payload));
|
|
11508
|
-
logger$
|
|
11710
|
+
logger$3.info('Transaction submitted', { transactionId: result.transactionId });
|
|
11509
11711
|
await this.showSuccessToast('Transaction Submitted', `Transaction ID: ${result.transactionId || 'Success'}`);
|
|
11510
11712
|
return result;
|
|
11511
11713
|
});
|
|
@@ -11524,7 +11726,7 @@ class TransactionService {
|
|
|
11524
11726
|
// Use the wallet's signAndExecuteTransaction method directly
|
|
11525
11727
|
// This uses hedera_signAndExecuteTransaction for WalletConnect (one prompt!)
|
|
11526
11728
|
const result = (await this.walletService.signAndExecuteTransaction(payload));
|
|
11527
|
-
logger$
|
|
11729
|
+
logger$3.info('Transaction signed and submitted', { transactionId: result.transactionId });
|
|
11528
11730
|
await this.showSuccessToast('Transaction Complete', `Transaction ID: ${result.transactionId || 'Success'}`);
|
|
11529
11731
|
return result;
|
|
11530
11732
|
});
|
|
@@ -11548,7 +11750,7 @@ class TransactionService {
|
|
|
11548
11750
|
batchKey: options.batchKey,
|
|
11549
11751
|
innerTransactions: options.innerTransactions,
|
|
11550
11752
|
}));
|
|
11551
|
-
logger$
|
|
11753
|
+
logger$3.info('Batch transaction submitted', { transactionId: result.transactionId });
|
|
11552
11754
|
await this.showSuccessToast('Batch Transaction Complete', `Transaction ID: ${result.transactionId || 'Success'}`);
|
|
11553
11755
|
return result;
|
|
11554
11756
|
});
|
|
@@ -11561,8 +11763,8 @@ class TransactionService {
|
|
|
11561
11763
|
*/
|
|
11562
11764
|
async signMessage(message) {
|
|
11563
11765
|
return this.executeTransaction(async () => {
|
|
11564
|
-
const result =
|
|
11565
|
-
logger$
|
|
11766
|
+
const result = await this.walletService.signMessage(message);
|
|
11767
|
+
logger$3.info('Message signed', { hasSignature: !!result.signature });
|
|
11566
11768
|
await this.showSuccessToast('Message Signed', 'Your message has been signed successfully.');
|
|
11567
11769
|
return result;
|
|
11568
11770
|
});
|
|
@@ -11685,6 +11887,378 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
|
|
|
11685
11887
|
args: [{ providedIn: 'root' }]
|
|
11686
11888
|
}] });
|
|
11687
11889
|
|
|
11890
|
+
/**
|
|
11891
|
+
* HSuite Native Connect
|
|
11892
|
+
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
11893
|
+
*
|
|
11894
|
+
* SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
|
11895
|
+
*
|
|
11896
|
+
* This file is part of HSuite Native Connect. For commercial licensing,
|
|
11897
|
+
* visit https://hsuite.finance/licensing
|
|
11898
|
+
*/
|
|
11899
|
+
/**
|
|
11900
|
+
* @file Smart-host customer session helper.
|
|
11901
|
+
*
|
|
11902
|
+
* @module services/smart-session
|
|
11903
|
+
*
|
|
11904
|
+
* @description
|
|
11905
|
+
* SmartSessionService automates the smart-host authentication handshake for
|
|
11906
|
+
* HSuite dApps. It discovers the host's `appId`, requests a challenge, signs it
|
|
11907
|
+
* with the active wallet account, and exchanges the signed challenge for a
|
|
11908
|
+
* short-lived bearer token.
|
|
11909
|
+
*
|
|
11910
|
+
* **Typical flow:**
|
|
11911
|
+
* ```typescript
|
|
11912
|
+
* const session = inject(SmartSessionService);
|
|
11913
|
+
* await session.login();
|
|
11914
|
+
* const bearer = session.getBearer();
|
|
11915
|
+
* ```
|
|
11916
|
+
*
|
|
11917
|
+
* The service also exposes {@link withSession} for automatic 401 recovery:
|
|
11918
|
+
* operations that fail with HTTP 401 are retried once after a fresh login.
|
|
11919
|
+
*/
|
|
11920
|
+
const logger$2 = getLogger().scoped?.('SmartSessionService') ?? getLogger();
|
|
11921
|
+
/**
|
|
11922
|
+
* Configuration token for {@link SmartSessionService}.
|
|
11923
|
+
*/
|
|
11924
|
+
const SMART_SESSION_CONFIG = new InjectionToken('SMART_SESSION_CONFIG', {
|
|
11925
|
+
factory: () => ({}),
|
|
11926
|
+
});
|
|
11927
|
+
/**
|
|
11928
|
+
* Smart-host customer session helper.
|
|
11929
|
+
*
|
|
11930
|
+
* @service SmartSessionService
|
|
11931
|
+
*
|
|
11932
|
+
* @description
|
|
11933
|
+
* Encapsulates the full challenge → sign → verify → store JWT flow required by
|
|
11934
|
+
* HSuite smart hosts. The service is intentionally thin: it delegates signing to
|
|
11935
|
+
* {@link TransactionService} and relies on standard Angular `HttpClient` for the
|
|
11936
|
+
* host HTTP calls.
|
|
11937
|
+
*
|
|
11938
|
+
* @providedIn root
|
|
11939
|
+
*/
|
|
11940
|
+
class SmartSessionService {
|
|
11941
|
+
http = inject(HttpClient);
|
|
11942
|
+
walletService = inject(UnifiedWalletService);
|
|
11943
|
+
transactionService = inject(TransactionService);
|
|
11944
|
+
config = inject(SMART_SESSION_CONFIG);
|
|
11945
|
+
get baseUrl() {
|
|
11946
|
+
return this.config.baseUrl ?? '';
|
|
11947
|
+
}
|
|
11948
|
+
get healthEndpoint() {
|
|
11949
|
+
return this.config.healthEndpoint ?? '/api/health';
|
|
11950
|
+
}
|
|
11951
|
+
get challengeEndpoint() {
|
|
11952
|
+
return this.config.challengeEndpoint ?? '/api/auth/challenge';
|
|
11953
|
+
}
|
|
11954
|
+
get verifyEndpoint() {
|
|
11955
|
+
return this.config.verifyEndpoint ?? '/api/auth/verify';
|
|
11956
|
+
}
|
|
11957
|
+
get storageKey() {
|
|
11958
|
+
return this.config.storageKey ?? 'hsuite.smart-session';
|
|
11959
|
+
}
|
|
11960
|
+
/**
|
|
11961
|
+
* Returns the currently stored bearer token, or `null` if there is no session.
|
|
11962
|
+
*
|
|
11963
|
+
* @returns Bearer token string or null
|
|
11964
|
+
*/
|
|
11965
|
+
getBearer() {
|
|
11966
|
+
const stored = this.readStoredSession();
|
|
11967
|
+
return stored && stored.expiresAt > Date.now() ? stored.bearer : null;
|
|
11968
|
+
}
|
|
11969
|
+
/**
|
|
11970
|
+
* Checks whether a non-expired session is available.
|
|
11971
|
+
*
|
|
11972
|
+
* @returns true if logged in and the bearer has not expired
|
|
11973
|
+
*/
|
|
11974
|
+
isLoggedIn() {
|
|
11975
|
+
return this.getBearer() !== null;
|
|
11976
|
+
}
|
|
11977
|
+
/**
|
|
11978
|
+
* Performs the full smart-host login handshake.
|
|
11979
|
+
*
|
|
11980
|
+
* @description
|
|
11981
|
+
* 1. Discovers `appId` from the health endpoint.
|
|
11982
|
+
* 2. Fetches a challenge from the challenge endpoint.
|
|
11983
|
+
* 3. Signs the challenge with the active wallet account.
|
|
11984
|
+
* 4. Exchanges the signed challenge for a bearer token.
|
|
11985
|
+
* 5. Stores the bearer and expiry in `sessionStorage`.
|
|
11986
|
+
*
|
|
11987
|
+
* @returns The verify response from the host
|
|
11988
|
+
*
|
|
11989
|
+
* @throws {Error} If no active account is available
|
|
11990
|
+
* @throws {Error} If the health endpoint does not return an `appId`
|
|
11991
|
+
* @throws {Error} If the challenge endpoint does not return a `challenge`
|
|
11992
|
+
* @throws {Error} If the verify call fails
|
|
11993
|
+
*/
|
|
11994
|
+
async login() {
|
|
11995
|
+
const account = this.walletService.activeAccount();
|
|
11996
|
+
if (!account) {
|
|
11997
|
+
throw new Error('No active account');
|
|
11998
|
+
}
|
|
11999
|
+
logger$2.info('Starting smart-host login handshake', {
|
|
12000
|
+
accountAddress: account.address,
|
|
12001
|
+
});
|
|
12002
|
+
const health = await this.fetchHealth();
|
|
12003
|
+
if (!health.appId) {
|
|
12004
|
+
throw new Error('Smart host health endpoint did not return appId');
|
|
12005
|
+
}
|
|
12006
|
+
const challengeResponse = await this.fetchChallenge(health.appId);
|
|
12007
|
+
if (!challengeResponse.challenge) {
|
|
12008
|
+
throw new Error('Smart host challenge endpoint did not return challenge');
|
|
12009
|
+
}
|
|
12010
|
+
const signResult = await this.transactionService.signMessage(challengeResponse.challenge);
|
|
12011
|
+
const verifyResponse = await this.verifyChallenge(health.appId, account, challengeResponse.challenge, signResult);
|
|
12012
|
+
if (!verifyResponse.bearer) {
|
|
12013
|
+
throw new Error('Smart host verify endpoint did not return bearer');
|
|
12014
|
+
}
|
|
12015
|
+
const expiresAt = this.resolveExpiry(verifyResponse);
|
|
12016
|
+
this.storeSession({ bearer: verifyResponse.bearer, expiresAt });
|
|
12017
|
+
logger$2.info('Smart-host login successful', {
|
|
12018
|
+
appId: health.appId,
|
|
12019
|
+
expiresAt,
|
|
12020
|
+
});
|
|
12021
|
+
return verifyResponse;
|
|
12022
|
+
}
|
|
12023
|
+
/**
|
|
12024
|
+
* Clears the stored session.
|
|
12025
|
+
*/
|
|
12026
|
+
logout() {
|
|
12027
|
+
this.clearStoredSession();
|
|
12028
|
+
logger$2.info('Smart-host session cleared');
|
|
12029
|
+
}
|
|
12030
|
+
/**
|
|
12031
|
+
* Runs an HTTP operation with automatic 401 recovery.
|
|
12032
|
+
*
|
|
12033
|
+
* @description
|
|
12034
|
+
* Executes the provided operation. If it rejects with an HTTP 401 error,
|
|
12035
|
+
* the service clears the current bearer, performs a fresh login, and retries
|
|
12036
|
+
* the operation once. Any other error is propagated immediately.
|
|
12037
|
+
*
|
|
12038
|
+
* @param operation - Async operation that needs a valid session
|
|
12039
|
+
* @returns The result of the operation
|
|
12040
|
+
*
|
|
12041
|
+
* @throws {Error} The original error if retry is not possible or fails
|
|
12042
|
+
*/
|
|
12043
|
+
async withSession(operation) {
|
|
12044
|
+
try {
|
|
12045
|
+
return await operation();
|
|
12046
|
+
}
|
|
12047
|
+
catch (error) {
|
|
12048
|
+
if (this.isUnauthorizedError(error)) {
|
|
12049
|
+
logger$2.warn('Operation received 401; attempting single session refresh');
|
|
12050
|
+
this.logout();
|
|
12051
|
+
await this.login();
|
|
12052
|
+
return await operation();
|
|
12053
|
+
}
|
|
12054
|
+
throw error;
|
|
12055
|
+
}
|
|
12056
|
+
}
|
|
12057
|
+
async fetchHealth() {
|
|
12058
|
+
return firstValueFrom(this.http.get(`${this.baseUrl}${this.healthEndpoint}`));
|
|
12059
|
+
}
|
|
12060
|
+
async fetchChallenge(appId) {
|
|
12061
|
+
return firstValueFrom(this.http.post(`${this.baseUrl}${this.challengeEndpoint}`, { appId }));
|
|
12062
|
+
}
|
|
12063
|
+
async verifyChallenge(appId, account, challenge, signResult) {
|
|
12064
|
+
const payload = {
|
|
12065
|
+
appId,
|
|
12066
|
+
accountAddress: account.address,
|
|
12067
|
+
publicKey: signResult.publicKey ?? account.publicKey,
|
|
12068
|
+
signature: signResult.signature,
|
|
12069
|
+
algorithm: signResult.algorithm,
|
|
12070
|
+
message: challenge,
|
|
12071
|
+
caipChainId: signResult.caipChainId,
|
|
12072
|
+
ledgerId: account.ledgerId,
|
|
12073
|
+
networkId: account.networkId,
|
|
12074
|
+
};
|
|
12075
|
+
return firstValueFrom(this.http.post(`${this.baseUrl}${this.verifyEndpoint}`, payload));
|
|
12076
|
+
}
|
|
12077
|
+
resolveExpiry(response) {
|
|
12078
|
+
if (response.expiresAt) {
|
|
12079
|
+
return response.expiresAt;
|
|
12080
|
+
}
|
|
12081
|
+
if (response.expiresIn) {
|
|
12082
|
+
return Date.now() + response.expiresIn * 1000;
|
|
12083
|
+
}
|
|
12084
|
+
// Default to 5 minutes if no expiry is provided
|
|
12085
|
+
return Date.now() + 5 * 60 * 1000;
|
|
12086
|
+
}
|
|
12087
|
+
readStoredSession() {
|
|
12088
|
+
try {
|
|
12089
|
+
const raw = sessionStorage.getItem(this.storageKey);
|
|
12090
|
+
if (!raw) {
|
|
12091
|
+
return null;
|
|
12092
|
+
}
|
|
12093
|
+
const parsed = JSON.parse(raw);
|
|
12094
|
+
if (!parsed.bearer || typeof parsed.expiresAt !== 'number') {
|
|
12095
|
+
return null;
|
|
12096
|
+
}
|
|
12097
|
+
return parsed;
|
|
12098
|
+
}
|
|
12099
|
+
catch {
|
|
12100
|
+
return null;
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
12103
|
+
storeSession(session) {
|
|
12104
|
+
try {
|
|
12105
|
+
sessionStorage.setItem(this.storageKey, JSON.stringify(session));
|
|
12106
|
+
}
|
|
12107
|
+
catch (error) {
|
|
12108
|
+
logger$2.warn('Failed to store smart session', { error });
|
|
12109
|
+
}
|
|
12110
|
+
}
|
|
12111
|
+
clearStoredSession() {
|
|
12112
|
+
try {
|
|
12113
|
+
sessionStorage.removeItem(this.storageKey);
|
|
12114
|
+
}
|
|
12115
|
+
catch (error) {
|
|
12116
|
+
logger$2.warn('Failed to clear smart session', { error });
|
|
12117
|
+
}
|
|
12118
|
+
}
|
|
12119
|
+
isUnauthorizedError(error) {
|
|
12120
|
+
if (!error || typeof error !== 'object') {
|
|
12121
|
+
return false;
|
|
12122
|
+
}
|
|
12123
|
+
const status = error.status;
|
|
12124
|
+
if (typeof status === 'number') {
|
|
12125
|
+
return status === 401;
|
|
12126
|
+
}
|
|
12127
|
+
const message = String(error.message ?? '');
|
|
12128
|
+
return /\b401\b|unauthorized/i.test(message);
|
|
12129
|
+
}
|
|
12130
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SmartSessionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
12131
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SmartSessionService, providedIn: 'root' });
|
|
12132
|
+
}
|
|
12133
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: SmartSessionService, decorators: [{
|
|
12134
|
+
type: Injectable,
|
|
12135
|
+
args: [{ providedIn: 'root' }]
|
|
12136
|
+
}] });
|
|
12137
|
+
|
|
12138
|
+
/**
|
|
12139
|
+
* HSuite Native Connect
|
|
12140
|
+
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
12141
|
+
*
|
|
12142
|
+
* SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
|
12143
|
+
*
|
|
12144
|
+
* This file is part of HSuite Native Connect. For commercial licensing,
|
|
12145
|
+
* visit https://hsuite.finance/licensing
|
|
12146
|
+
*/
|
|
12147
|
+
/**
|
|
12148
|
+
* @file HTTP interceptor that attaches wallet-attributed auth headers.
|
|
12149
|
+
*
|
|
12150
|
+
* @module services/hsuite-auth-interceptor
|
|
12151
|
+
*
|
|
12152
|
+
* @description
|
|
12153
|
+
* `HsuiteAuthInterceptor` automatically adds the active smart-host bearer token
|
|
12154
|
+
* and wallet ledger identifier to outgoing HTTP requests. When a request fails
|
|
12155
|
+
* with HTTP 401, it delegates recovery to {@link SmartSessionService} for a
|
|
12156
|
+
* single re-login and retry.
|
|
12157
|
+
*
|
|
12158
|
+
* **Registration (standalone apps):**
|
|
12159
|
+
* ```typescript
|
|
12160
|
+
* provideHttpClient(withInterceptors([provideHsuiteAuthInterceptor()]))
|
|
12161
|
+
* ```
|
|
12162
|
+
*
|
|
12163
|
+
* **Registration (module-based apps):**
|
|
12164
|
+
* ```typescript
|
|
12165
|
+
* { provide: HTTP_INTERCEPTORS, useClass: HsuiteAuthInterceptor, multi: true }
|
|
12166
|
+
* ```
|
|
12167
|
+
*/
|
|
12168
|
+
/**
|
|
12169
|
+
* Headers injected by the interceptor.
|
|
12170
|
+
*/
|
|
12171
|
+
const HSUITE_AUTH_HEADER = 'Authorization';
|
|
12172
|
+
const HSUITE_LEDGER_HEADER = 'X-Ledger-Id';
|
|
12173
|
+
/**
|
|
12174
|
+
* Functional HTTP interceptor that attaches wallet-attributed auth headers.
|
|
12175
|
+
*
|
|
12176
|
+
* @description
|
|
12177
|
+
* Adds:
|
|
12178
|
+
* - `Authorization: Bearer <token>` when {@link SmartSessionService.getBearer} returns a token
|
|
12179
|
+
* - `X-Ledger-Id: <ledgerId>` when a wallet account is active
|
|
12180
|
+
*
|
|
12181
|
+
* On 401 responses, clears the stale bearer and retries once after re-login.
|
|
12182
|
+
*
|
|
12183
|
+
* @param req - The outgoing HTTP request
|
|
12184
|
+
* @param next - The downstream request handler
|
|
12185
|
+
* @returns Observable of HTTP events
|
|
12186
|
+
*/
|
|
12187
|
+
const hsuiteAuthInterceptor = (req, next) => {
|
|
12188
|
+
const session = inject(SmartSessionService);
|
|
12189
|
+
const wallet = inject(UnifiedWalletService);
|
|
12190
|
+
const authenticated = appendAuthHeaders(req, session, wallet);
|
|
12191
|
+
return next(authenticated).pipe(catchError((error) => {
|
|
12192
|
+
if (error instanceof HttpErrorResponse && error.status === 401) {
|
|
12193
|
+
session.logout();
|
|
12194
|
+
return from(session.login()).pipe(switchMap(() => next(appendAuthHeaders(req, session, wallet))), catchError((loginError) => throwError(() => loginError)));
|
|
12195
|
+
}
|
|
12196
|
+
return throwError(() => error);
|
|
12197
|
+
}));
|
|
12198
|
+
};
|
|
12199
|
+
/**
|
|
12200
|
+
* Class-based HTTP interceptor for module-based Angular apps.
|
|
12201
|
+
*
|
|
12202
|
+
* @description
|
|
12203
|
+
* Provides the same behavior as {@link hsuiteAuthInterceptor} in a class form
|
|
12204
|
+
* suitable for registration via `HTTP_INTERCEPTORS`.
|
|
12205
|
+
*/
|
|
12206
|
+
class HsuiteAuthInterceptor {
|
|
12207
|
+
session = inject(SmartSessionService);
|
|
12208
|
+
wallet = inject(UnifiedWalletService);
|
|
12209
|
+
/**
|
|
12210
|
+
* Intercepts outgoing HTTP requests to attach wallet-attributed auth headers.
|
|
12211
|
+
*
|
|
12212
|
+
* @param req - The outgoing HTTP request
|
|
12213
|
+
* @param next - The downstream request handler
|
|
12214
|
+
* @returns Observable of HTTP events
|
|
12215
|
+
*/
|
|
12216
|
+
intercept(req, next) {
|
|
12217
|
+
const authenticated = appendAuthHeaders(req, this.session, this.wallet);
|
|
12218
|
+
return next.handle(authenticated).pipe(catchError((error) => {
|
|
12219
|
+
if (error instanceof HttpErrorResponse && error.status === 401) {
|
|
12220
|
+
this.session.logout();
|
|
12221
|
+
return from(this.session.login()).pipe(switchMap(() => next.handle(appendAuthHeaders(req, this.session, this.wallet))), catchError((loginError) => throwError(() => loginError)));
|
|
12222
|
+
}
|
|
12223
|
+
return throwError(() => error);
|
|
12224
|
+
}));
|
|
12225
|
+
}
|
|
12226
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: HsuiteAuthInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
12227
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: HsuiteAuthInterceptor, providedIn: 'root' });
|
|
12228
|
+
}
|
|
12229
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: HsuiteAuthInterceptor, decorators: [{
|
|
12230
|
+
type: Injectable,
|
|
12231
|
+
args: [{ providedIn: 'root' }]
|
|
12232
|
+
}] });
|
|
12233
|
+
/**
|
|
12234
|
+
* Helper that clones a request with the active auth headers attached.
|
|
12235
|
+
*
|
|
12236
|
+
* @param req - The outgoing HTTP request
|
|
12237
|
+
* @param session - Smart session service for bearer lookup
|
|
12238
|
+
* @param wallet - Unified wallet service for ledger ID lookup
|
|
12239
|
+
* @returns The request cloned with Authorization and/or X-Ledger-Id headers
|
|
12240
|
+
*/
|
|
12241
|
+
function appendAuthHeaders(req, session, wallet) {
|
|
12242
|
+
const bearer = session.getBearer();
|
|
12243
|
+
const activeAccount = wallet.activeAccount();
|
|
12244
|
+
const ledgerId = activeAccount?.ledgerId;
|
|
12245
|
+
// Avoid mutating requests that already carry an Authorization header
|
|
12246
|
+
if (!bearer && !ledgerId) {
|
|
12247
|
+
return req;
|
|
12248
|
+
}
|
|
12249
|
+
const headers = {};
|
|
12250
|
+
if (bearer && !req.headers.has(HSUITE_AUTH_HEADER)) {
|
|
12251
|
+
headers[HSUITE_AUTH_HEADER] = `Bearer ${bearer}`;
|
|
12252
|
+
}
|
|
12253
|
+
if (ledgerId && !req.headers.has(HSUITE_LEDGER_HEADER)) {
|
|
12254
|
+
headers[HSUITE_LEDGER_HEADER] = ledgerId;
|
|
12255
|
+
}
|
|
12256
|
+
if (Object.keys(headers).length === 0) {
|
|
12257
|
+
return req;
|
|
12258
|
+
}
|
|
12259
|
+
return req.clone({ setHeaders: headers });
|
|
12260
|
+
}
|
|
12261
|
+
|
|
11688
12262
|
/**
|
|
11689
12263
|
* HSuite Native Connect
|
|
11690
12264
|
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
@@ -12125,6 +12699,43 @@ class BaseTransactionBuilderService {
|
|
|
12125
12699
|
}
|
|
12126
12700
|
}
|
|
12127
12701
|
|
|
12702
|
+
/**
|
|
12703
|
+
* HSuite Native Connect
|
|
12704
|
+
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
12705
|
+
*
|
|
12706
|
+
* SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
|
12707
|
+
*
|
|
12708
|
+
* This file is part of HSuite Native Connect. For commercial licensing,
|
|
12709
|
+
* visit https://hsuite.finance/licensing
|
|
12710
|
+
*/
|
|
12711
|
+
/**
|
|
12712
|
+
* @file ACTIVE_ACCOUNT_SOURCE: the minimal active-account read surface the
|
|
12713
|
+
* transaction builders need.
|
|
12714
|
+
*
|
|
12715
|
+
* @description
|
|
12716
|
+
* The Hedera transaction builder only reads `activeAccount()` from the wallet
|
|
12717
|
+
* (to pick the network and the entity key config). Injecting the whole
|
|
12718
|
+
* {@link UnifiedWalletService} couples the builder to a heavy root service that
|
|
12719
|
+
* also runs the dApp connect stack on construction. This token narrows that
|
|
12720
|
+
* dependency to a single member.
|
|
12721
|
+
*
|
|
12722
|
+
* The default provider points at {@link UnifiedWalletService}, so existing SDK
|
|
12723
|
+
* consumers that already provide it need NO wiring change (backward
|
|
12724
|
+
* compatible). The embedded widget shell overrides this token with a one-member
|
|
12725
|
+
* account context, which keeps the heavy root service from ever being
|
|
12726
|
+
* constructed inside the wallet.
|
|
12727
|
+
*/
|
|
12728
|
+
/**
|
|
12729
|
+
* Injection token for the active-account read surface. Defaults to
|
|
12730
|
+
* {@link UnifiedWalletService} so consumers that already provide it need no
|
|
12731
|
+
* extra wiring; override it to supply a narrower source (the embedded shell
|
|
12732
|
+
* does this to avoid constructing the root service inside the wallet).
|
|
12733
|
+
*/
|
|
12734
|
+
const ACTIVE_ACCOUNT_SOURCE = new InjectionToken('ACTIVE_ACCOUNT_SOURCE', {
|
|
12735
|
+
providedIn: 'root',
|
|
12736
|
+
factory: () => inject(UnifiedWalletService),
|
|
12737
|
+
});
|
|
12738
|
+
|
|
12128
12739
|
/**
|
|
12129
12740
|
* HSuite Native Connect
|
|
12130
12741
|
* Copyright 2024-2025 HSuite (https://hsuite.finance)
|
|
@@ -12268,7 +12879,7 @@ function assertSafeInteger(value, context) {
|
|
|
12268
12879
|
* ```
|
|
12269
12880
|
*/
|
|
12270
12881
|
class HederaTransactionBuilderService {
|
|
12271
|
-
|
|
12882
|
+
accountSource = inject(ACTIVE_ACCOUNT_SOURCE);
|
|
12272
12883
|
logger = inject(LoggerService).scoped('HederaTransactionBuilder');
|
|
12273
12884
|
/**
|
|
12274
12885
|
* Gets the network ID from the active wallet account.
|
|
@@ -12283,7 +12894,7 @@ class HederaTransactionBuilderService {
|
|
|
12283
12894
|
* @throws {Error} When used in methods that require a network, if no account is active
|
|
12284
12895
|
*/
|
|
12285
12896
|
getNetworkId() {
|
|
12286
|
-
const activeAccount = this.
|
|
12897
|
+
const activeAccount = this.accountSource.activeAccount();
|
|
12287
12898
|
return activeAccount?.networkId;
|
|
12288
12899
|
}
|
|
12289
12900
|
/**
|
|
@@ -12329,7 +12940,7 @@ class HederaTransactionBuilderService {
|
|
|
12329
12940
|
* @returns Key configuration or undefined if no active account
|
|
12330
12941
|
*/
|
|
12331
12942
|
getActiveAccountKeyConfig() {
|
|
12332
|
-
const activeAccount = this.
|
|
12943
|
+
const activeAccount = this.accountSource.activeAccount();
|
|
12333
12944
|
if (!activeAccount) {
|
|
12334
12945
|
this.logger.debug('No active account for key config');
|
|
12335
12946
|
return undefined;
|
|
@@ -14588,5 +15199,5 @@ function provideWalletErrorHandler(config) {
|
|
|
14588
15199
|
* Generated bundle index. Do not edit.
|
|
14589
15200
|
*/
|
|
14590
15201
|
|
|
14591
|
-
export { AccountActionsComponent, AccountFilterComponent, AccountFormattingService, AccountGroupingService, AccountListComponent, AccountSelectorComponent, AccountSelectorService, BaseWalletProvider, ChannelClientService, ChromeExtensionTransport, DEFAULT_LEDGER_ICONS, DEFAULT_WALLET_URL, HederaSigner, HederaTransactionBuilderService, HsuiteNativeProvider, HsuiteWalletModule, LEDGER_COLORS, LEDGER_NAMES, LoggerService, P2PNativeProvider, P2PSessionManager, SignerFactory, TransactionService, UnifiedWalletService, WalletAccountDisplayComponent, WalletConnectButtonComponent, WalletConnectClientManager, WalletConnectPromptComponent, WalletConnectSessionStore, WalletConnectSigningOrchestrator, WalletConnectV2Provider, WalletConnectedDirective, WalletConnectedGuardComponent, WalletConnectionModalComponent, WalletContextDirective, WalletContextService, WalletErrorHandler, WalletEventBus, WalletEventsDirective, WalletSessionDisplayComponent, WalletTransactionStatusComponent, XrplSigner, XrplTransactionBuilderService, getExtensionVersion, getLedgerColor, getLedgerIcon, getLedgerName, getTokenIcon, isExtensionInstalled, isHsuiteNativeConfig, isWalletConnectV2Config, provideWalletErrorHandler, scaleHederaAmountToBaseUnits };
|
|
15202
|
+
export { ACTIVE_ACCOUNT_SOURCE, AccountActionsComponent, AccountFilterComponent, AccountFormattingService, AccountGroupingService, AccountListComponent, AccountSelectorComponent, AccountSelectorService, BaseWalletProvider, ChannelClientService, ChromeExtensionTransport, DEFAULT_LEDGER_ICONS, DEFAULT_WALLET_URL, HSUITE_AUTH_HEADER, HSUITE_LEDGER_HEADER, HederaSigner, HederaTransactionBuilderService, HsuiteAuthInterceptor, HsuiteNativeProvider, HsuiteWalletModule, LEDGER_COLORS, LEDGER_ICON_NAMES, LEDGER_NAMES, LedgerUIRegistry, LoggerService, P2PNativeProvider, P2PSessionManager, SMART_SESSION_CONFIG, SignerFactory, SmartSessionService, TransactionService, UnifiedWalletService, WalletAccountDisplayComponent, WalletConnectButtonComponent, WalletConnectClientManager, WalletConnectPromptComponent, WalletConnectSessionStore, WalletConnectSigningOrchestrator, WalletConnectV2Provider, WalletConnectedDirective, WalletConnectedGuardComponent, WalletConnectionModalComponent, WalletContextDirective, WalletContextService, WalletErrorHandler, WalletEventBus, WalletEventsDirective, WalletSessionDisplayComponent, WalletTransactionStatusComponent, XrplSigner, XrplTransactionBuilderService, getExtensionVersion, getLedgerColor, getLedgerIcon, getLedgerName, getTokenIcon, hsuiteAuthInterceptor, isExtensionInstalled, isHsuiteNativeConfig, isWalletConnectV2Config, provideWalletErrorHandler, registerLedgerIcons, scaleHederaAmountToBaseUnits };
|
|
14592
15203
|
//# sourceMappingURL=hsuite-native-connect-angular.mjs.map
|