From a4d33b2928ce3bdb210e5d1e2df741aa3fb20545 Mon Sep 17 00:00:00 2001 From: Rhys Bartels-Waller Date: Tue, 28 Jan 2025 11:29:41 +0000 Subject: [PATCH 1/5] refactor: remove Blockfrost provider experiments With the retirement of most cardano-services, Blockfrost providers are now the only option for these, as updated here. --- .../src/lib/scripts/background/config.ts | 6 --- .../providers/ExperimentsProvider/config.ts | 30 -------------- .../providers/ExperimentsProvider/types.ts | 6 --- packages/cardano/src/wallet/lib/providers.ts | 39 ++++--------------- 4 files changed, 8 insertions(+), 73 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts index f697469bb7..33380467b7 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts @@ -62,12 +62,6 @@ export const getProviders = async (chainName: Wallet.ChainName): Promise ({ [ExperimentName.USE_SWITCH_TO_NAMI_MODE]: false, [ExperimentName.SHARED_WALLETS]: false, [ExperimentName.WEBSOCKET_API]: false, - [ExperimentName.BLOCKFROST_ASSET_PROVIDER]: false, - [ExperimentName.BLOCKFROST_CHAIN_HISTORY_PROVIDER]: false, - [ExperimentName.BLOCKFROST_NETWORK_INFO_PROVIDER]: false, - [ExperimentName.BLOCKFROST_REWARDS_PROVIDER]: false, - [ExperimentName.BLOCKFROST_TX_SUBMIT_PROVIDER]: false, - [ExperimentName.BLOCKFROST_UTXO_PROVIDER]: false, [ExperimentName.BLOCKFROST_ADDRESS_DISCOVERY]: false, [ExperimentName.BLOCKFROST_INPUT_RESOLVER]: false, [ExperimentName.EXTENSION_STORAGE]: false, @@ -40,30 +34,6 @@ export const experiments: ExperimentsConfig = { value: false, default: false }, - [ExperimentName.BLOCKFROST_ASSET_PROVIDER]: { - value: false, - default: false - }, - [ExperimentName.BLOCKFROST_CHAIN_HISTORY_PROVIDER]: { - value: false, - default: false - }, - [ExperimentName.BLOCKFROST_NETWORK_INFO_PROVIDER]: { - value: false, - default: false - }, - [ExperimentName.BLOCKFROST_REWARDS_PROVIDER]: { - value: false, - default: false - }, - [ExperimentName.BLOCKFROST_TX_SUBMIT_PROVIDER]: { - value: false, - default: false - }, - [ExperimentName.BLOCKFROST_UTXO_PROVIDER]: { - value: false, - default: false - }, [ExperimentName.BLOCKFROST_ADDRESS_DISCOVERY]: { value: false, default: false diff --git a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts index fbc81eec58..d9ee6e1ac3 100644 --- a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts +++ b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts @@ -11,12 +11,6 @@ export enum ExperimentName { USE_SWITCH_TO_NAMI_MODE = 'use-switch-to-nami-mode', SHARED_WALLETS = 'shared-wallets', WEBSOCKET_API = 'websocket-api', - BLOCKFROST_ASSET_PROVIDER = 'blockfrost-asset-provider', - BLOCKFROST_CHAIN_HISTORY_PROVIDER = 'blockfrost-chain-history-provider', - BLOCKFROST_NETWORK_INFO_PROVIDER = 'blockfrost-network-info-provider', - BLOCKFROST_REWARDS_PROVIDER = 'blockfrost-rewards-provider', - BLOCKFROST_TX_SUBMIT_PROVIDER = 'blockfrost-tx-submit-provider', - BLOCKFROST_UTXO_PROVIDER = 'blockfrost-utxo-provider', BLOCKFROST_ADDRESS_DISCOVERY = 'blockfrost-address-discovery', BLOCKFROST_INPUT_RESOLVER = 'blockfrost-input-resolver', EXTENSION_STORAGE = 'extension-storage', diff --git a/packages/cardano/src/wallet/lib/providers.ts b/packages/cardano/src/wallet/lib/providers.ts index 1f87f180ea..6dbc4c164d 100644 --- a/packages/cardano/src/wallet/lib/providers.ts +++ b/packages/cardano/src/wallet/lib/providers.ts @@ -20,14 +20,8 @@ import type { DRepInfo } from '@cardano-sdk/core'; import { CardanoWsClient, CreateHttpProviderConfig, - assetInfoHttpProvider, - chainHistoryHttpProvider, - networkInfoHttpProvider, - rewardsHttpProvider, stakePoolHttpProvider, - utxoHttpProvider, TxSubmitApiProvider, - txSubmitHttpProvider, BlockfrostClientConfig, RateLimiter, BlockfrostClient, @@ -47,6 +41,7 @@ import { WalletProvidersDependencies } from './cardano-wallet'; import { BlockfrostInputResolver } from './blockfrost-input-resolver'; const createTxSubmitProvider = ( + blockfrostClient: BlockfrostClient, httpProviderConfig: CreateHttpProviderConfig, customSubmitTxUrl?: string ): TxSubmitProvider => { @@ -61,7 +56,7 @@ const createTxSubmitProvider = ( ); } - return txSubmitHttpProvider(httpProviderConfig); + return new BlockfrostTxSubmitProvider(blockfrostClient, httpProviderConfig.logger); }; export type AllProviders = { @@ -115,12 +110,6 @@ export const createProviders = ({ env: { baseCardanoServicesUrl: baseUrl, customSubmitTxUrl, blockfrostConfig }, logger, experiments: { - useBlockfrostAssetProvider, - useBlockfrostChainHistoryProvider, - useBlockfrostNetworkInfoProvider, - useBlockfrostRewardsProvider, - useBlockfrostTxSubmitProvider, - useBlockfrostUtxoProvider, useDrepProviderOverrideActiveStatus, useBlockfrostAddressDiscovery, useBlockfrostInputResolver, @@ -134,22 +123,12 @@ export const createProviders = ({ const blockfrostClient = new BlockfrostClient(blockfrostConfig, { rateLimiter: blockfrostConfig.rateLimiter }); - const assetProvider = useBlockfrostAssetProvider - ? new BlockfrostAssetProvider(blockfrostClient, logger) - : assetInfoHttpProvider(httpProviderConfig); - const networkInfoProvider = useBlockfrostNetworkInfoProvider - ? new BlockfrostNetworkInfoProvider(blockfrostClient, logger) - : networkInfoHttpProvider(httpProviderConfig); - const chainHistoryProvider = useBlockfrostChainHistoryProvider - ? new BlockfrostChainHistoryProvider(blockfrostClient, networkInfoProvider, logger) - : chainHistoryHttpProvider(httpProviderConfig); - const rewardsProvider = useBlockfrostRewardsProvider - ? new BlockfrostRewardsProvider(blockfrostClient, logger) - : rewardsHttpProvider(httpProviderConfig); + const assetProvider = new BlockfrostAssetProvider(blockfrostClient, logger); + const networkInfoProvider = new BlockfrostNetworkInfoProvider(blockfrostClient, logger); + const chainHistoryProvider = new BlockfrostChainHistoryProvider(blockfrostClient, networkInfoProvider, logger); + const rewardsProvider = new BlockfrostRewardsProvider(blockfrostClient, logger); const stakePoolProvider = stakePoolHttpProvider(httpProviderConfig); - const txSubmitProvider = useBlockfrostTxSubmitProvider - ? new BlockfrostTxSubmitProvider(blockfrostClient, logger) - : createTxSubmitProvider(httpProviderConfig, customSubmitTxUrl); + const txSubmitProvider = createTxSubmitProvider(blockfrostClient, httpProviderConfig, customSubmitTxUrl); const dRepProvider = new BlockfrostDRepProvider(blockfrostClient, logger); const addressDiscovery = useBlockfrostAddressDiscovery @@ -221,9 +200,7 @@ export const createProviders = ({ }; } - const utxoProvider = useBlockfrostUtxoProvider - ? new BlockfrostUtxoProvider(blockfrostClient, logger) - : utxoHttpProvider(httpProviderConfig); + const utxoProvider = new BlockfrostUtxoProvider(blockfrostClient, logger); return { assetProvider, From 00ae726383286856d7f269634e1c28500d2b8609 Mon Sep 17 00:00:00 2001 From: Rhys Bartels-Waller Date: Tue, 28 Jan 2025 11:38:46 +0000 Subject: [PATCH 2/5] refactor: remove other Blockfrost experiments These address discovery and input resolvers are optimised for Blockfrost API providers, which are now hard coded. --- .../src/lib/scripts/background/config.ts | 4 +--- .../src/providers/ExperimentsProvider/config.ts | 10 ---------- .../src/providers/ExperimentsProvider/types.ts | 2 -- packages/cardano/src/wallet/lib/providers.ts | 14 +++----------- 4 files changed, 4 insertions(+), 26 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts index 33380467b7..f00464a080 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts @@ -61,9 +61,7 @@ export const getProviders = async (chainName: Wallet.ChainName): Promise ({ [ExperimentName.USE_SWITCH_TO_NAMI_MODE]: false, [ExperimentName.SHARED_WALLETS]: false, [ExperimentName.WEBSOCKET_API]: false, - [ExperimentName.BLOCKFROST_ADDRESS_DISCOVERY]: false, - [ExperimentName.BLOCKFROST_INPUT_RESOLVER]: false, [ExperimentName.EXTENSION_STORAGE]: false, [ExperimentName.USE_DREP_PROVIDER_OVERRIDE]: false, [ExperimentName.DAPP_EXPLORER]: false @@ -34,14 +32,6 @@ export const experiments: ExperimentsConfig = { value: false, default: false }, - [ExperimentName.BLOCKFROST_ADDRESS_DISCOVERY]: { - value: false, - default: false - }, - [ExperimentName.BLOCKFROST_INPUT_RESOLVER]: { - value: false, - default: false - }, [ExperimentName.EXTENSION_STORAGE]: { value: false, default: false diff --git a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts index d9ee6e1ac3..a8795d038f 100644 --- a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts +++ b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts @@ -11,8 +11,6 @@ export enum ExperimentName { USE_SWITCH_TO_NAMI_MODE = 'use-switch-to-nami-mode', SHARED_WALLETS = 'shared-wallets', WEBSOCKET_API = 'websocket-api', - BLOCKFROST_ADDRESS_DISCOVERY = 'blockfrost-address-discovery', - BLOCKFROST_INPUT_RESOLVER = 'blockfrost-input-resolver', EXTENSION_STORAGE = 'extension-storage', USE_DREP_PROVIDER_OVERRIDE = 'use-drep-provider-override', DAPP_EXPLORER = 'dapp-explorer' diff --git a/packages/cardano/src/wallet/lib/providers.ts b/packages/cardano/src/wallet/lib/providers.ts index 6dbc4c164d..0176ba1e09 100644 --- a/packages/cardano/src/wallet/lib/providers.ts +++ b/packages/cardano/src/wallet/lib/providers.ts @@ -36,7 +36,6 @@ import { } from '@cardano-sdk/cardano-services-client'; import { RemoteApiProperties, RemoteApiPropertyType } from '@cardano-sdk/web-extension'; import { BlockfrostAddressDiscovery } from '@wallet/lib/blockfrost-address-discovery'; -import { DEFAULT_LOOK_AHEAD_SEARCH, HDSequentialDiscovery } from '@cardano-sdk/wallet'; import { WalletProvidersDependencies } from './cardano-wallet'; import { BlockfrostInputResolver } from './blockfrost-input-resolver'; @@ -109,12 +108,7 @@ export const createProviders = ({ axiosAdapter, env: { baseCardanoServicesUrl: baseUrl, customSubmitTxUrl, blockfrostConfig }, logger, - experiments: { - useDrepProviderOverrideActiveStatus, - useBlockfrostAddressDiscovery, - useBlockfrostInputResolver, - useWebSocket - } + experiments: { useDrepProviderOverrideActiveStatus, useWebSocket } }: ProvidersConfig): WalletProvidersDependencies => { if (!logger) logger = console; @@ -131,9 +125,7 @@ export const createProviders = ({ const txSubmitProvider = createTxSubmitProvider(blockfrostClient, httpProviderConfig, customSubmitTxUrl); const dRepProvider = new BlockfrostDRepProvider(blockfrostClient, logger); - const addressDiscovery = useBlockfrostAddressDiscovery - ? new BlockfrostAddressDiscovery(blockfrostClient, logger) - : new HDSequentialDiscovery(chainHistoryProvider, DEFAULT_LOOK_AHEAD_SEARCH); + const addressDiscovery = new BlockfrostAddressDiscovery(blockfrostClient, logger); const rewardAccountInfoProvider = new BlockfrostRewardAccountInfoProvider({ client: blockfrostClient, @@ -142,7 +134,7 @@ export const createProviders = ({ stakePoolProvider }); - const inputResolver = useBlockfrostInputResolver ? new BlockfrostInputResolver(blockfrostClient, logger) : undefined; + const inputResolver = new BlockfrostInputResolver(blockfrostClient, logger); // Temporary proxy for drepProvider to overwrite the 'active' property to always be true const drepProviderOverrideActiveStatus = new Proxy(dRepProvider, { From 38efa68dc58882b496e1564a5f7d0215679dcc29 Mon Sep 17 00:00:00 2001 From: Rhys Bartels-Waller Date: Tue, 28 Jan 2025 11:47:02 +0000 Subject: [PATCH 3/5] refactor: cleanup extension storage experiment --- .../src/lib/scripts/background/wallet.ts | 18 ++++++------------ .../providers/ExperimentsProvider/config.ts | 5 ----- .../src/providers/ExperimentsProvider/types.ts | 1 - 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts index 89db9fd925..944a634165 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts @@ -77,19 +77,13 @@ type StoredWallet = AnyWallet; const createWalletRepositoryStore = (): Observable> => from( (async () => { - // wallet repository is always using feature flag of 'mainnet', because it has to be the same for all networks - const featureFlags = await getFeatureFlags(Wallet.Cardano.ChainIds.Mainnet.networkMagic); - if (isExperimentEnabled(featureFlags, ExperimentName.EXTENSION_STORAGE)) { - const extensionStore = new ExtensionBlobCollectionStore('walletRepository', logger); - const wallets = await firstValueFrom(extensionStore.getAll().pipe(defaultIfEmpty(null))); - if (!wallets) { - const pouchdbStore = createPouchdbWalletRepositoryStore(); - await migrateCollectionStore(pouchdbStore, extensionStore, logger); - } - return extensionStore; + const extensionStore = new ExtensionBlobCollectionStore('walletRepository', logger); + const wallets = await firstValueFrom(extensionStore.getAll().pipe(defaultIfEmpty(null))); + if (!wallets) { + const pouchdbStore = createPouchdbWalletRepositoryStore(); + await migrateCollectionStore(pouchdbStore, extensionStore, logger); } - - return createPouchdbWalletRepositoryStore(); + return extensionStore; })() ); diff --git a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/config.ts b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/config.ts index d36b0c95de..ea1edb9fa0 100644 --- a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/config.ts +++ b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/config.ts @@ -6,7 +6,6 @@ export const getDefaultFeatureFlags = (): FallbackConfiguration => ({ [ExperimentName.USE_SWITCH_TO_NAMI_MODE]: false, [ExperimentName.SHARED_WALLETS]: false, [ExperimentName.WEBSOCKET_API]: false, - [ExperimentName.EXTENSION_STORAGE]: false, [ExperimentName.USE_DREP_PROVIDER_OVERRIDE]: false, [ExperimentName.DAPP_EXPLORER]: false }); @@ -32,10 +31,6 @@ export const experiments: ExperimentsConfig = { value: false, default: false }, - [ExperimentName.EXTENSION_STORAGE]: { - value: false, - default: false - }, [ExperimentName.USE_DREP_PROVIDER_OVERRIDE]: { value: false, default: false diff --git a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts index a8795d038f..423aaf6c66 100644 --- a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts +++ b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts @@ -11,7 +11,6 @@ export enum ExperimentName { USE_SWITCH_TO_NAMI_MODE = 'use-switch-to-nami-mode', SHARED_WALLETS = 'shared-wallets', WEBSOCKET_API = 'websocket-api', - EXTENSION_STORAGE = 'extension-storage', USE_DREP_PROVIDER_OVERRIDE = 'use-drep-provider-override', DAPP_EXPLORER = 'dapp-explorer' } From b8592c144002f33febc4db3968cc43df7d8c075b Mon Sep 17 00:00:00 2001 From: Rhys Bartels-Waller Date: Tue, 28 Jan 2025 12:01:43 +0000 Subject: [PATCH 4/5] refactor: drop DRepProvider active override This was a temporary patch to workaround a backend issue --- .../src/lib/scripts/background/config.ts | 1 - .../providers/ExperimentsProvider/config.ts | 5 --- .../providers/ExperimentsProvider/types.ts | 1 - packages/cardano/src/wallet/lib/providers.ts | 44 ++----------------- 4 files changed, 3 insertions(+), 48 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts index f00464a080..9356278599 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts @@ -60,7 +60,6 @@ export const getProviders = async (chainName: Wallet.ChainName): Promise ({ [ExperimentName.USE_SWITCH_TO_NAMI_MODE]: false, [ExperimentName.SHARED_WALLETS]: false, [ExperimentName.WEBSOCKET_API]: false, - [ExperimentName.USE_DREP_PROVIDER_OVERRIDE]: false, [ExperimentName.DAPP_EXPLORER]: false }); @@ -31,10 +30,6 @@ export const experiments: ExperimentsConfig = { value: false, default: false }, - [ExperimentName.USE_DREP_PROVIDER_OVERRIDE]: { - value: false, - default: false - }, [ExperimentName.DAPP_EXPLORER]: { value: false, default: false diff --git a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts index 423aaf6c66..812bcec8a8 100644 --- a/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts +++ b/apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts @@ -11,7 +11,6 @@ export enum ExperimentName { USE_SWITCH_TO_NAMI_MODE = 'use-switch-to-nami-mode', SHARED_WALLETS = 'shared-wallets', WEBSOCKET_API = 'websocket-api', - USE_DREP_PROVIDER_OVERRIDE = 'use-drep-provider-override', DAPP_EXPLORER = 'dapp-explorer' } diff --git a/packages/cardano/src/wallet/lib/providers.ts b/packages/cardano/src/wallet/lib/providers.ts index 0176ba1e09..ef40ee8fa1 100644 --- a/packages/cardano/src/wallet/lib/providers.ts +++ b/packages/cardano/src/wallet/lib/providers.ts @@ -15,7 +15,6 @@ import { TxSubmitProvider, UtxoProvider } from '@cardano-sdk/core'; -import type { DRepInfo } from '@cardano-sdk/core'; import { CardanoWsClient, @@ -86,15 +85,6 @@ interface ProvidersConfig { logger?: Logger; experiments: { useWebSocket?: boolean; - useBlockfrostAssetProvider?: boolean; - useDrepProviderOverrideActiveStatus?: boolean; - useBlockfrostChainHistoryProvider?: boolean; - useBlockfrostNetworkInfoProvider?: boolean; - useBlockfrostRewardsProvider?: boolean; - useBlockfrostTxSubmitProvider?: boolean; - useBlockfrostUtxoProvider?: boolean; - useBlockfrostAddressDiscovery?: boolean; - useBlockfrostInputResolver?: boolean; }; } @@ -108,7 +98,7 @@ export const createProviders = ({ axiosAdapter, env: { baseCardanoServicesUrl: baseUrl, customSubmitTxUrl, blockfrostConfig }, logger, - experiments: { useDrepProviderOverrideActiveStatus, useWebSocket } + experiments: { useWebSocket } }: ProvidersConfig): WalletProvidersDependencies => { if (!logger) logger = console; @@ -136,34 +126,6 @@ export const createProviders = ({ const inputResolver = new BlockfrostInputResolver(blockfrostClient, logger); - // Temporary proxy for drepProvider to overwrite the 'active' property to always be true - const drepProviderOverrideActiveStatus = new Proxy(dRepProvider, { - get(target, property, receiver) { - const original = Reflect.get(target, property, receiver); - if (property === 'getDRepInfo') { - return async function (...args: any[]) { - const response: DRepInfo = await original.apply(target, args); - return { - ...response, - active: true - }; - }; - } - - if (property === 'getDRepsInfo') { - return async function (...args: any[]) { - const response: DRepInfo[] = await original.apply(target, args); - return response.map((drepInfo) => ({ - ...drepInfo, - active: true - })); - }; - } - - return original; - } - }); - if (useWebSocket) { const url = new URL(baseUrl); @@ -188,7 +150,7 @@ export const createProviders = ({ wsProvider, addressDiscovery, inputResolver, - drepProvider: useDrepProviderOverrideActiveStatus ? drepProviderOverrideActiveStatus : dRepProvider + drepProvider: dRepProvider }; } @@ -205,7 +167,7 @@ export const createProviders = ({ rewardsProvider, addressDiscovery, inputResolver, - drepProvider: useDrepProviderOverrideActiveStatus ? drepProviderOverrideActiveStatus : dRepProvider + drepProvider: dRepProvider }; }; From 3961a370a480434db7fd6d2e86e14f50907ff3a3 Mon Sep 17 00:00:00 2001 From: Martynas Date: Tue, 28 Jan 2025 14:12:20 +0200 Subject: [PATCH 5/5] feat: pause polling while not using Lace LW-12174 (#1673) * feat: pause polling while not using Lace * chore: update SDK packages * chore: update session timeout in env defaults --------- Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> --- apps/browser-extension-wallet/.env.defaults | 3 ++ apps/browser-extension-wallet/.env.example | 3 ++ apps/browser-extension-wallet/package.json | 4 +- apps/browser-extension-wallet/src/config.ts | 9 +++- .../src/lib/scripts/background/cip30.ts | 10 +++- .../src/lib/scripts/background/config.ts | 4 +- .../lib/scripts/background/session/index.ts | 4 ++ .../background/session/is-lace-popup-open.ts | 12 +++++ .../background/session/is-lace-tab-active.ts | 34 ++++++++++++ .../session/notify-on-have-access-call.ts | 14 +++++ .../session/user-session-tracker.ts | 52 +++++++++++++++++++ .../src/lib/scripts/background/wallet.ts | 27 +++++++++- apps/browser-extension-wallet/src/popup.tsx | 9 +++- .../src/utils/constants.ts | 2 + packages/cardano/package.json | 4 +- packages/core/package.json | 4 +- packages/nami/package.json | 2 +- packages/staking/package.json | 8 +-- yarn.lock | 40 +++++++------- 19 files changed, 206 insertions(+), 39 deletions(-) create mode 100644 apps/browser-extension-wallet/src/lib/scripts/background/session/index.ts create mode 100644 apps/browser-extension-wallet/src/lib/scripts/background/session/is-lace-popup-open.ts create mode 100644 apps/browser-extension-wallet/src/lib/scripts/background/session/is-lace-tab-active.ts create mode 100644 apps/browser-extension-wallet/src/lib/scripts/background/session/notify-on-have-access-call.ts create mode 100644 apps/browser-extension-wallet/src/lib/scripts/background/session/user-session-tracker.ts diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index 3a96140a84..345b2aa900 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -120,3 +120,6 @@ MIDNIGHT_EVENT_BANNER_REMINDER_TIME=129600000 # Shared Wallet SHARED_WALLET_TX_VALIDITY_INTERVAL=24 MIN_NUMBER_OF_COSIGNERS=2 + +# POLLING PAUSE AFTER INACTIVITY +SESSION_TIMEOUT=300000 diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index 9cb08c5e29..73dd313681 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -109,3 +109,6 @@ MIDNIGHT_EVENT_BANNER_REMINDER_TIME=129600000 # Shared Wallet SHARED_WALLET_TX_VALIDITY_INTERVAL=24 MIN_NUMBER_OF_COSIGNERS=2 + +# POLLING PAUSE AFTER INACTIVITY +SESSION_TIMEOUT=300000 diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index e528acacab..9bf94611d3 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -48,8 +48,8 @@ "@cardano-sdk/tx-construction": "0.26.0", "@cardano-sdk/util": "0.15.5", "@cardano-sdk/util-rxjs": "0.9.4", - "@cardano-sdk/wallet": "0.51.4", - "@cardano-sdk/web-extension": "0.38.4", + "@cardano-sdk/wallet": "0.51.5", + "@cardano-sdk/web-extension": "0.38.5", "@emurgo/cip14-js": "~3.0.1", "@input-output-hk/lace-ui-toolkit": "1.21.0", "@lace/cardano": "0.1.0", diff --git a/apps/browser-extension-wallet/src/config.ts b/apps/browser-extension-wallet/src/config.ts index 6fa4734568..4c187e32f5 100644 --- a/apps/browser-extension-wallet/src/config.ts +++ b/apps/browser-extension-wallet/src/config.ts @@ -33,6 +33,7 @@ export type Config = { SAVED_PRICE_DURATION: number; DEFAULT_SUBMIT_API: string; GOV_TOOLS_URLS: Record; + SESSION_TIMEOUT: Milliseconds; }; // eslint-disable-next-line complexity @@ -143,6 +144,12 @@ export const config = (): Config => { Preprod: `${process.env.GOV_TOOLS_URL_PREPROD}`, Preview: `${process.env.GOV_TOOLS_URL_PREVIEW}`, Sanchonet: `${process.env.GOV_TOOLS_URL_SANCHONET}` - } + }, + // eslint-disable-next-line new-cap + SESSION_TIMEOUT: Milliseconds( + !Number.isNaN(Number.parseInt(process.env.SESSION_TIMEOUT)) + ? Number.parseInt(process.env.SESSION_TIMEOUT) + : 1000 * 60 * 5 + ) }; }; diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/cip30.ts b/apps/browser-extension-wallet/src/lib/scripts/background/cip30.ts index 46fc202705..32ec12b82a 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/cip30.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/cip30.ts @@ -2,7 +2,7 @@ import { cip30 as walletCip30 } from '@cardano-sdk/wallet'; import { ensureUiIsOpenAndLoaded } from './util'; import { userPromptService } from './services/dappService'; import { authenticator } from './authenticator'; -import { wallet$ } from './wallet'; +import { wallet$, dAppConnectorActivity$ } from './wallet'; import { runtime, Tabs, tabs } from 'webextension-polyfill'; import { exposeApi, RemoteApiPropertyType, cip30 } from '@cardano-sdk/web-extension'; import { DAPP_CHANNELS } from '../../../utils/constants'; @@ -14,6 +14,7 @@ import pDebounce from 'p-debounce'; import { dappInfo$ } from './requestAccess'; import { senderToDappInfo } from '@src/utils/senderToDappInfo'; import { getBackgroundStorage } from './storage'; +import { notifyOnHaveAccessCall } from './session'; const DEBOUNCE_THROTTLE = 500; @@ -108,7 +109,12 @@ const walletApi = walletCip30.createWalletApi( cip30.initializeBackgroundScript( { walletName: process.env.WALLET_NAME }, - { authenticator, logger: console, runtime, walletApi } + { + authenticator: notifyOnHaveAccessCall(authenticator, dAppConnectorActivity$.next.bind(dAppConnectorActivity$)), + logger: console, + runtime, + walletApi + } ); exposeApi( diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts index 9356278599..14f9c5be17 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/config.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/config.ts @@ -30,7 +30,7 @@ export const backgroundServiceProperties: RemoteApiProperties unhandledError$: RemoteApiPropertyType.HotObservable }; -const { BLOCKFROST_CONFIGS, BLOCKFROST_RATE_LIMIT_CONFIG } = config(); +const { BLOCKFROST_CONFIGS, BLOCKFROST_RATE_LIMIT_CONFIG, SESSION_TIMEOUT } = config(); // Important to use the same rateLimiter object for all networks, // because Blockfrost rate limit is per IP address, not per project id export const rateLimiter: RateLimiter = new Bottleneck({ @@ -84,3 +84,5 @@ export const userIdServiceProperties: RemoteApiProperties ports.size > 0), + distinctUntilChanged(), + share() +); diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/session/is-lace-tab-active.ts b/apps/browser-extension-wallet/src/lib/scripts/background/session/is-lace-tab-active.ts new file mode 100644 index 0000000000..cacdfe5413 --- /dev/null +++ b/apps/browser-extension-wallet/src/lib/scripts/background/session/is-lace-tab-active.ts @@ -0,0 +1,34 @@ +import { LACE_EXTENSION_ID } from '@src/features/nami-migration/migration-tool/cross-extension-messaging/nami/environment'; +import { distinctUntilChanged, from, fromEventPattern, map, merge, share, startWith, switchMap } from 'rxjs'; +import { Tabs, tabs, windows } from 'webextension-polyfill'; + +type WindowId = number; + +const windowRemoved$ = fromEventPattern( + (handler) => windows.onRemoved.addListener(handler), + (handler) => windows.onRemoved.removeListener(handler) +); +const tabUpdated$ = fromEventPattern( + (handler) => tabs.onUpdated.addListener(handler), + (handler) => tabs.onUpdated.removeListener(handler), + (tabId: number, changeInfo: Tabs.OnUpdatedChangeInfoType, tab: Tabs.Tab) => ({ tabId, changeInfo, tab }) +); +const tabActivated$ = fromEventPattern( + (handler) => tabs.onActivated.addListener(handler), + (handler) => tabs.onActivated.removeListener(handler) +); + +export const isLaceTabActive$ = merge(windowRemoved$, tabUpdated$, tabActivated$).pipe( + switchMap(() => + from( + tabs.query({ + active: true, + url: `chrome-extension://${LACE_EXTENSION_ID}/*` + }) + ) + ), + map((activeLaceTabs) => activeLaceTabs.length > 0), + startWith(false), + distinctUntilChanged(), + share() +); diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/session/notify-on-have-access-call.ts b/apps/browser-extension-wallet/src/lib/scripts/background/session/notify-on-have-access-call.ts new file mode 100644 index 0000000000..606db604de --- /dev/null +++ b/apps/browser-extension-wallet/src/lib/scripts/background/session/notify-on-have-access-call.ts @@ -0,0 +1,14 @@ +import { AuthenticatorApi } from '@cardano-sdk/dapp-connector'; + +/** + * cip30 messaging is calling authenticator `haveAccess` on every cip30 request + * we're using it to keep track whether dapp connector is currently active + */ +export const notifyOnHaveAccessCall = (authenticator: AuthenticatorApi, notify: () => void): AuthenticatorApi => ({ + requestAccess: authenticator.requestAccess.bind(authenticator), + revokeAccess: authenticator.revokeAccess.bind(authenticator), + haveAccess(...args) { + notify(); + return authenticator.haveAccess(...args); + } +}); diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/session/user-session-tracker.ts b/apps/browser-extension-wallet/src/lib/scripts/background/session/user-session-tracker.ts new file mode 100644 index 0000000000..d0c1bc4418 --- /dev/null +++ b/apps/browser-extension-wallet/src/lib/scripts/background/session/user-session-tracker.ts @@ -0,0 +1,52 @@ +import { Milliseconds } from '@cardano-sdk/core'; +import { + combineLatest, + distinctUntilChanged, + EMPTY, + filter, + map, + merge, + Observable, + share, + startWith, + switchMap, + timer, + Timestamp +} from 'rxjs'; + +export type UserSessionStatus = { + isSessionActive: boolean; + isLacePopupOpen: boolean; + isLaceTabActive: boolean; + lastDappConnectorActivityAt: Timestamp; +}; + +export const createUserSessionTracker = ( + isLacePopupOpen$: Observable, + isLaceTabActive$: Observable, + dAppConnectorActivity$: Observable, + timeout: Milliseconds +): Observable => { + const sharedDappConnectorActivity$ = dAppConnectorActivity$.pipe(share()); + const isAnyUiActive$ = combineLatest([isLacePopupOpen$, isLaceTabActive$]).pipe( + map(([isLacePopupOpen, isLaceTabActive]) => isLacePopupOpen || isLaceTabActive), + distinctUntilChanged(), + share() + ); + const on$ = merge(isAnyUiActive$.pipe(filter(Boolean)), sharedDappConnectorActivity$.pipe(map(() => true))); + const off$ = isAnyUiActive$.pipe( + switchMap((isAnyUiActive) => { + if (isAnyUiActive) { + // never go inactive while any UI is active + return EMPTY; + } + return sharedDappConnectorActivity$.pipe( + startWith(void 0), + switchMap(() => timer(timeout)), + map(() => false) + ); + }) + ); + + return merge(on$, off$).pipe(startWith(false), distinctUntilChanged()); +}; diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts index 944a634165..f2d9281295 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts @@ -1,7 +1,19 @@ /* eslint-disable unicorn/no-null */ import { runtime, storage as webStorage } from 'webextension-polyfill'; -import { of, combineLatest, map, EMPTY, BehaviorSubject, Observable, from, firstValueFrom, defaultIfEmpty } from 'rxjs'; -import { getProviders } from './config'; +import { + of, + combineLatest, + map, + EMPTY, + BehaviorSubject, + Observable, + from, + firstValueFrom, + defaultIfEmpty, + Subject, + tap +} from 'rxjs'; +import { getProviders, SESSION_TIMEOUT } from './config'; import { DEFAULT_POLLING_CONFIG, createPersonalWallet, storage, createSharedWallet } from '@cardano-sdk/wallet'; import { handleHttpProvider } from '@cardano-sdk/cardano-services-client'; import { Cardano, HandleProvider } from '@cardano-sdk/core'; @@ -36,6 +48,15 @@ import { ExtensionDocumentStore } from './storage/extension-document-store'; import { ExtensionBlobKeyValueStore } from './storage/extension-blob-key-value-store'; import { ExtensionBlobCollectionStore } from './storage/extension-blob-collection-store'; import { migrateCollectionStore, migrateWalletStores, shouldAttemptWalletStoresMigration } from './storage/migrations'; +import { isLacePopupOpen$, createUserSessionTracker, isLaceTabActive$ } from './session'; +import { TrackerSubject } from '@cardano-sdk/util-rxjs'; + +export const dAppConnectorActivity$ = new Subject(); +const pollController$ = new TrackerSubject( + createUserSessionTracker(isLacePopupOpen$, isLaceTabActive$, dAppConnectorActivity$, SESSION_TIMEOUT).pipe( + tap((isActive) => logger.debug('Session active:', isActive)) + ) +); if (typeof window !== 'undefined') { throw new TypeError('This module should only be imported in service worker'); @@ -143,6 +164,7 @@ const walletFactory: WalletFactory { const [mode, setMode] = useState<'lace' | 'nami'>(); @@ -87,3 +89,6 @@ const App = (): React.ReactElement => { const mountNode = document.querySelector('#lace-popup'); ReactDOM.render(, mountNode); + +// not exposing any API; used to keep track of connection with SW to determine whether popup is open +createNonBackgroundMessenger({ baseChannel: TRACK_POPUP_CHANNEL }, { logger, runtime }); diff --git a/apps/browser-extension-wallet/src/utils/constants.ts b/apps/browser-extension-wallet/src/utils/constants.ts index 375bf296ab..8b934f05d3 100644 --- a/apps/browser-extension-wallet/src/utils/constants.ts +++ b/apps/browser-extension-wallet/src/utils/constants.ts @@ -115,3 +115,5 @@ export const COINGECKO_URL = 'https://www.coingecko.com'; export const MULTIDELEGATION_FIRST_VISIT_LS_KEY = 'multidelegationFirstVisit'; export const MULTIDELEGATION_DAPP_COMPATIBILITY_LS_KEY = 'isMultiDelegationDAppCompatibilityModalVisible'; export const STAKING_BROWSER_PREFERENCES_LS_KEY = 'stakingBrowserPreferences'; + +export const TRACK_POPUP_CHANNEL = 'popup'; diff --git a/packages/cardano/package.json b/packages/cardano/package.json index 9d670f8c0e..a0be16cb4d 100644 --- a/packages/cardano/package.json +++ b/packages/cardano/package.json @@ -48,8 +48,8 @@ "@cardano-sdk/key-management": "0.27.0", "@cardano-sdk/tx-construction": "0.26.0", "@cardano-sdk/util": "0.15.5", - "@cardano-sdk/wallet": "0.51.4", - "@cardano-sdk/web-extension": "0.38.4", + "@cardano-sdk/wallet": "0.51.5", + "@cardano-sdk/web-extension": "0.38.5", "@lace/common": "0.1.0", "@ledgerhq/devices": "^8.4.4", "@stablelib/chacha20poly1305": "1.0.1", diff --git a/packages/core/package.json b/packages/core/package.json index 5cd3137ba7..5e0097a54e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -43,8 +43,8 @@ }, "dependencies": { "@ant-design/icons": "^4.7.0", - "@cardano-sdk/wallet": "0.51.4", - "@cardano-sdk/web-extension": "0.38.4", + "@cardano-sdk/wallet": "0.51.5", + "@cardano-sdk/web-extension": "0.38.5", "@input-output-hk/lace-ui-toolkit": "1.19.0", "@lace/cardano": "0.1.0", "@lace/common": "0.1.0", diff --git a/packages/nami/package.json b/packages/nami/package.json index a59a514e39..49e3c611e8 100644 --- a/packages/nami/package.json +++ b/packages/nami/package.json @@ -61,7 +61,7 @@ "@cardano-sdk/crypto": "0.2.0", "@cardano-sdk/tx-construction": "0.26.0", "@cardano-sdk/util": "0.15.5", - "@cardano-sdk/web-extension": "0.38.4", + "@cardano-sdk/web-extension": "0.38.5", "@chakra-ui/css-reset": "1.0.0", "@chakra-ui/icons": "1.0.13", "@chakra-ui/react": "1.6.4", diff --git a/packages/staking/package.json b/packages/staking/package.json index b7bd7232b1..1c9d856efa 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -80,8 +80,8 @@ "@cardano-sdk/input-selection": "0.14.1", "@cardano-sdk/tx-construction": "0.26.0", "@cardano-sdk/util": "0.15.5", - "@cardano-sdk/wallet": "0.51.4", - "@cardano-sdk/web-extension": "0.38.4", + "@cardano-sdk/wallet": "0.51.5", + "@cardano-sdk/web-extension": "0.38.5", "@storybook/addon-actions": "^7.6.7", "@storybook/addon-essentials": "^7.6.7", "@storybook/addon-interactions": "^7.6.7", @@ -129,8 +129,8 @@ "@cardano-sdk/input-selection": "0.14.1", "@cardano-sdk/tx-construction": "0.26.0", "@cardano-sdk/util": "0.15.5", - "@cardano-sdk/wallet": "0.51.4", - "@cardano-sdk/web-extension": "0.38.4", + "@cardano-sdk/wallet": "0.51.5", + "@cardano-sdk/web-extension": "0.38.5", "@lace/cardano": "^0.1.0", "@lace/common": "^0.1.0", "@lace/core": "0.1.0", diff --git a/yarn.lock b/yarn.lock index d97c8a6abd..5507929ac0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9370,9 +9370,9 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/wallet@npm:0.51.4, @cardano-sdk/wallet@npm:~0.51.4": - version: 0.51.4 - resolution: "@cardano-sdk/wallet@npm:0.51.4" +"@cardano-sdk/wallet@npm:0.51.5, @cardano-sdk/wallet@npm:~0.51.5": + version: 0.51.5 + resolution: "@cardano-sdk/wallet@npm:0.51.5" dependencies: "@cardano-sdk/core": ~0.45.0 "@cardano-sdk/crypto": ~0.2.0 @@ -9394,13 +9394,13 @@ __metadata: ts-custom-error: ^3.2.0 ts-log: ^2.2.3 uuid: ^8.3.2 - checksum: 432835c9d00d4893e0473b8acd8812465bed0d3ace73a504326eb099a891fed976add5623bf349211cc2ad23c2e9e79b7d0c73a687089b366903a3a07ba12287 + checksum: 9b59314b3d538714dfb111314d5ea514b7481a7eb28d2ad36fb02d6a92f4d2c4df5f7e21b92a1e4ec4eeb9fdae66b62c1422c0a16aa9887b1144cef0c8732aa2 languageName: node linkType: hard -"@cardano-sdk/web-extension@npm:0.38.4": - version: 0.38.4 - resolution: "@cardano-sdk/web-extension@npm:0.38.4" +"@cardano-sdk/web-extension@npm:0.38.5": + version: 0.38.5 + resolution: "@cardano-sdk/web-extension@npm:0.38.5" dependencies: "@cardano-sdk/core": ~0.45.0 "@cardano-sdk/crypto": ~0.2.0 @@ -9411,7 +9411,7 @@ __metadata: "@cardano-sdk/tx-construction": ~0.26.0 "@cardano-sdk/util": ~0.15.5 "@cardano-sdk/util-rxjs": ~0.9.4 - "@cardano-sdk/wallet": ~0.51.4 + "@cardano-sdk/wallet": ~0.51.5 backoff-rxjs: ^7.0.0 lodash: ^4.17.21 rxjs: ^7.4.0 @@ -9419,7 +9419,7 @@ __metadata: ts-log: ^2.2.3 uuid: ^8.3.2 webextension-polyfill: ^0.8.0 - checksum: 3f6a690f35e4aba5c90e8207557f142fe2310f01907fae699ac2b0af531784a592deb97fadfba28c44982bbff56a6c93e53a0dc7b235be0539a30f1e0fab87bc + checksum: 01da34f1db0a26c2bf4895e7bcc65aa66cc3381811ad7877b08b2ee55b22952f975af525b21af915383e6b31cc3c6c42c5261ac891cc8ed3b3b5a115e667a76a languageName: node linkType: hard @@ -13493,8 +13493,8 @@ __metadata: "@cardano-sdk/tx-construction": 0.26.0 "@cardano-sdk/util": 0.15.5 "@cardano-sdk/util-rxjs": 0.9.4 - "@cardano-sdk/wallet": 0.51.4 - "@cardano-sdk/web-extension": 0.38.4 + "@cardano-sdk/wallet": 0.51.5 + "@cardano-sdk/web-extension": 0.38.5 "@emurgo/cardano-message-signing-asmjs": 1.0.1 "@emurgo/cip14-js": ~3.0.1 "@input-output-hk/lace-ui-toolkit": 1.21.0 @@ -13587,8 +13587,8 @@ __metadata: "@cardano-sdk/tx-construction": 0.26.0 "@cardano-sdk/util": 0.15.5 "@cardano-sdk/util-dev": 0.25.3 - "@cardano-sdk/wallet": 0.51.4 - "@cardano-sdk/web-extension": 0.38.4 + "@cardano-sdk/wallet": 0.51.5 + "@cardano-sdk/web-extension": 0.38.5 "@emurgo/cardano-message-signing-browser": 1.0.1 "@lace/common": 0.1.0 "@ledgerhq/devices": ^8.4.4 @@ -13653,8 +13653,8 @@ __metadata: "@babel/preset-env": ^7.22.20 "@babel/preset-react": ^7.22.15 "@babel/preset-typescript": ^7.22.15 - "@cardano-sdk/wallet": 0.51.4 - "@cardano-sdk/web-extension": 0.38.4 + "@cardano-sdk/wallet": 0.51.5 + "@cardano-sdk/web-extension": 0.38.5 "@input-output-hk/lace-ui-toolkit": 1.19.0 "@lace/cardano": 0.1.0 "@lace/common": 0.1.0 @@ -13753,7 +13753,7 @@ __metadata: "@cardano-sdk/crypto": 0.2.0 "@cardano-sdk/tx-construction": 0.26.0 "@cardano-sdk/util": 0.15.5 - "@cardano-sdk/web-extension": 0.38.4 + "@cardano-sdk/web-extension": 0.38.5 "@chakra-ui/css-reset": 1.0.0 "@chakra-ui/icons": 1.0.13 "@chakra-ui/react": 1.6.4 @@ -13836,8 +13836,8 @@ __metadata: "@cardano-sdk/input-selection": 0.14.1 "@cardano-sdk/tx-construction": 0.26.0 "@cardano-sdk/util": 0.15.5 - "@cardano-sdk/wallet": 0.51.4 - "@cardano-sdk/web-extension": 0.38.4 + "@cardano-sdk/wallet": 0.51.5 + "@cardano-sdk/web-extension": 0.38.5 "@input-output-hk/lace-ui-toolkit": 1.19.0 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 @@ -13905,8 +13905,8 @@ __metadata: "@cardano-sdk/input-selection": 0.14.1 "@cardano-sdk/tx-construction": 0.26.0 "@cardano-sdk/util": 0.15.5 - "@cardano-sdk/wallet": 0.51.4 - "@cardano-sdk/web-extension": 0.38.4 + "@cardano-sdk/wallet": 0.51.5 + "@cardano-sdk/web-extension": 0.38.5 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 "@lace/core": 0.1.0