diff --git a/packages/desktop/components/EmptyListPlaceholder.svelte b/packages/desktop/components/EmptyListPlaceholder.svelte index 6b2aeea75d..93c3c6d4ec 100644 --- a/packages/desktop/components/EmptyListPlaceholder.svelte +++ b/packages/desktop/components/EmptyListPlaceholder.svelte @@ -1,7 +1,7 @@ @@ -10,12 +10,16 @@ -
- {title} - {#if subtitle} - {subtitle} - {/if} -
+ {#if title || subtitle} +
+ {#if title} + {title} + {/if} + {#if subtitle} + {subtitle} + {/if} +
+ {/if} diff --git a/packages/desktop/views/dashboard/wallet/panes/account-summary/components/AccountNetworkSummary.svelte b/packages/desktop/views/dashboard/wallet/panes/account-summary/components/AccountNetworkSummary.svelte index 39bce617b9..a722775974 100644 --- a/packages/desktop/views/dashboard/wallet/panes/account-summary/components/AccountNetworkSummary.svelte +++ b/packages/desktop/views/dashboard/wallet/panes/account-summary/components/AccountNetworkSummary.svelte @@ -21,6 +21,7 @@ import { selectedAccountTokens } from '@core/token/stores' import { ownedNfts } from '@core/nfts/stores' import { getFiatValueFromTokenAmount } from '@core/market/actions' + import { notificationsManager } from '@auxiliary/wallet-connect/notifications' export let network: Network export let account: IAccountState @@ -86,6 +87,13 @@ try { await generateAndStoreEvmAddressForAccounts($activeProfile.type, network.coinType, account) + if (account.index === 0 && $activeProfile.type === ProfileType.Software) { + try { + await notificationsManager.registerAccount(account, network.id, network.coinType) + } catch (error) { + console.error(error) + } + } pollEvmBalancesForAccount($activeProfile.id, account) if ($activeProfile.type === ProfileType.Ledger) { setSelectedNetworkForNetworkDrawer(network) diff --git a/packages/desktop/webpack.config.ts b/packages/desktop/webpack.config.ts index 2cf9a921c0..ef8e1e9d18 100644 --- a/packages/desktop/webpack.config.ts +++ b/packages/desktop/webpack.config.ts @@ -30,6 +30,9 @@ const appId = stage === 'prod' ? 'org.bloom-labs.bloom' : `org.bloom-labs.bloom. const appProtocol = stage === 'prod' ? 'bloom' : `bloom-${stage.toLowerCase()}` +const WALLETCONNECT_PROJECT_ID = + mode !== 'development' ? process.env.WALLETCONNECT_PROJECT_ID : '41511f9b50c46a80cdf8bd1a3532f2f9' + // / ------------------------ Resolve ------------------------ const fallback: { [index: string]: string | false | string[] } = { @@ -184,6 +187,7 @@ const rendererPlugins = [ new MiniCssExtractPlugin({ filename: '[name].css', }), + new DefinePlugin({ 'process.env.PLATFORM': JSON.stringify(process.env.PLATFORM || 'desktop'), 'process.platform': JSON.stringify(process.platform), @@ -191,7 +195,7 @@ const rendererPlugins = [ features: JSON.stringify(features), PRELOAD_SCRIPT: JSON.stringify(false), 'process.env.APP_PROTOCOL': JSON.stringify(appProtocol), - 'process.env.WALLETCONNECT_PROJECT_ID': JSON.stringify(process.env.WALLETCONNECT_PROJECT_ID), + 'process.env.WALLETCONNECT_PROJECT_ID': JSON.stringify(WALLETCONNECT_PROJECT_ID), }), // The ethereumjs libraries require the NormalModuleReplacementPlugin & the ProvidePlugin new NormalModuleReplacementPlugin(/node:/, (resource) => { diff --git a/packages/shared/package.json b/packages/shared/package.json index 1b12792276..b2b9750b1c 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -5,7 +5,7 @@ "author": "Bloom Labs Ltd ", "license": "PolyForm Strict License 1.0.0", "dependencies": { - "@bloomwalletio/ui": "0.21.3", + "@bloomwalletio/ui": "0.21.5", "@ethereumjs/common": "4.3.0", "@ethereumjs/rlp": "5.0.2", "@ethereumjs/tx": "5.3.0", @@ -17,7 +17,8 @@ "@spruceid/siwe-parser": "2.0.2", "@sveltejs/svelte-virtual-list": "3.0.1", "@walletconnect/jsonrpc-types": "1.0.4", - "@walletconnect/types": "2.11.3", + "@walletconnect/notify-client": "1.4.1", + "@walletconnect/types": "2.12.2", "@walletconnect/web3wallet": "1.11.2", "http-status-codes": "2.3.0", "lottie-web": "5.12.2", diff --git a/packages/shared/src/components/avatars/NotificationAvatar.svelte b/packages/shared/src/components/avatars/NotificationAvatar.svelte new file mode 100644 index 0000000000..1c2a042ea3 --- /dev/null +++ b/packages/shared/src/components/avatars/NotificationAvatar.svelte @@ -0,0 +1,66 @@ + + +
+
+ + {#if hasDappImage} + {subscription?.metadata?.name} (dappImageError = true)} + /> + {/if} + +
+ {#if subscription?.metadata.name} + + {/if} + {#if notificationType && subscription?.scope[notificationType]} + + + {#if !notificationImageError} + {notificationType} (notificationImageError = true)} + /> + {/if} + + + + {/if} +
diff --git a/packages/shared/src/components/avatars/index.ts b/packages/shared/src/components/avatars/index.ts index 6145818df2..9266cdb09c 100644 --- a/packages/shared/src/components/avatars/index.ts +++ b/packages/shared/src/components/avatars/index.ts @@ -3,6 +3,7 @@ export { default as GovernanceAvatar } from './GovernanceAvatar.svelte' export { default as NftAvatar } from './NftAvatar.svelte' export { default as NetworkAvatar } from './NetworkAvatar.svelte' export { default as NetworkAvatarGroup } from './NetworkAvatarGroup.svelte' +export { default as NotificationAvatar } from './NotificationAvatar.svelte' export { default as ProfileAvatar } from './ProfileAvatar.svelte' export { default as ProfileAvatarWithBadge } from './ProfileAvatarWithBadge.svelte' export { default as TokenAvatar } from './TokenAvatar.svelte' diff --git a/packages/shared/src/components/molecules/NotificationSubscriptionInfo.svelte b/packages/shared/src/components/molecules/NotificationSubscriptionInfo.svelte new file mode 100644 index 0000000000..560bbbaea3 --- /dev/null +++ b/packages/shared/src/components/molecules/NotificationSubscriptionInfo.svelte @@ -0,0 +1,44 @@ + + + +
+ {#if metadata?.icons?.[0]} + {metadata.name} + {:else} + + {/if} +
+
+ + {metadata?.name} + + {#if metadata?.appDomain} + + {/if} +
+ + + {metadata?.description} + +
+
+ +
+ + diff --git a/packages/shared/src/components/molecules/index.ts b/packages/shared/src/components/molecules/index.ts index 80fda9b0b9..de382d0e37 100644 --- a/packages/shared/src/components/molecules/index.ts +++ b/packages/shared/src/components/molecules/index.ts @@ -9,6 +9,7 @@ export { default as NftGallery } from './NftGallery.svelte' export { default as NftGalleryItem } from './NftGalleryItem.svelte' export { default as NftMedia } from './NftMedia.svelte' export { default as NodeAuthTab } from './NodeAuthTab.svelte' +export { default as NotificationSubscriptionInfo } from './NotificationSubscriptionInfo.svelte' export { default as ShimmerClaimingAccountList } from './ShimmerClaimingAccountList.svelte' export { default as TransactionAssetSection } from './TransactionAssetSection.svelte' diff --git a/packages/shared/src/components/tiles/NotificationTile.svelte b/packages/shared/src/components/tiles/NotificationTile.svelte new file mode 100644 index 0000000000..26a2f4c10e --- /dev/null +++ b/packages/shared/src/components/tiles/NotificationTile.svelte @@ -0,0 +1,45 @@ + + + onClick(notification, subscription)} +> +
+ +
+
+
+ {notification.title} +
+ {#if !notification.isRead} + + {:else} + + {getBestTimeDuration($time.getTime() - notification.sentAt, 'day', true)} + + {/if} +
+
+ {notification.body} +
+
+
+
diff --git a/packages/shared/src/components/tiles/index.ts b/packages/shared/src/components/tiles/index.ts index 7c775b1b28..17069a266a 100644 --- a/packages/shared/src/components/tiles/index.ts +++ b/packages/shared/src/components/tiles/index.ts @@ -2,6 +2,7 @@ export { default as AliasTile } from './AliasTile.svelte' export { default as TokenAmountTile } from './TokenAmountTile.svelte' export { default as TokenAvailableBalanceTile } from './TokenAvailableBalanceTile.svelte' export { default as ClickableTile } from './ClickableTile.svelte' +export { default as NotificationTile } from './NotificationTile.svelte' export { default as NftTile } from './NftTile.svelte' export { default as ShimmerClaimingAccountTile } from './ShimmerClaimingAccountTile.svelte' export { default as Tile } from './Tile.svelte' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/actions/buildSupportedNamespaceFromSelections.ts b/packages/shared/src/lib/auxiliary/wallet-connect/actions/buildSupportedNamespaceFromSelections.ts index 18d9a17e41..39a72b1961 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/actions/buildSupportedNamespaceFromSelections.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/actions/buildSupportedNamespaceFromSelections.ts @@ -1,9 +1,9 @@ import { ProposalTypes } from '@walletconnect/types' import { GENERAL_SUPPORTED_METHODS, SUPPORTED_EVENTS } from '../constants' -import { getAddressFromAccountForNetwork } from '@core/account/utils' -import { NetworkId } from '@core/network/types' +import { EvmNetworkId } from '@core/network/types' import { ISelections } from '../interface' import { ISupportedNamespace, SupportedNamespaces } from '../types' +import { getCaip10AddressForAccount } from '../utils' export function buildSupportedNamespacesFromSelections( selections: ISelections, @@ -49,9 +49,8 @@ function buildSupportedNamespace( addresses = allowedChains.flatMap((evmNetwork) => { return ( selections.accounts - ?.map((account) => getAddressFromAccountForNetwork(account, evmNetwork as NetworkId)) - .filter(Boolean) - .map((address) => `${evmNetwork}:${address}`) ?? [] + ?.map((account) => getCaip10AddressForAccount(account, evmNetwork as EvmNetworkId) ?? '') + .filter(Boolean) ?? [] ) }) } else { diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/actions/initializeWalletConnect.ts b/packages/shared/src/lib/auxiliary/wallet-connect/actions/initializeWalletConnect.ts index 81c2834812..45dfa7ba93 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/actions/initializeWalletConnect.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/actions/initializeWalletConnect.ts @@ -1,31 +1,39 @@ import { handleError } from '@core/error/handlers' -import features from '@features/features' -import { Core } from '@walletconnect/core' import { Web3Wallet } from '@walletconnect/web3wallet' import { get } from 'svelte/store' +import { WALLET_CONNECT_CORE } from '../constants/wallet-connect-core.constant' import { WALLET_METADATA } from '../constants' import { onSessionDelete, onSessionProposal, onSessionRequest } from '../handlers' -import { walletClient } from '../stores' +import { walletClient } from '../stores/wallet-client.store' import { setConnectedDapps } from '../stores/connected-dapps.store' +import { isFeatureEnabled } from '@lib/features/utils' +import { notificationsManager } from '../notifications' export async function initializeWalletConnect(): Promise { - if (!features?.walletConnect?.enabled || get(walletClient)) { + if (isFeatureEnabled('walletConnect.web3Wallet')) { + await initializeWalletClient() + } + if (isFeatureEnabled('walletConnect.notifications')) { + await notificationsManager.init() + } +} + +async function initializeWalletClient(): Promise { + if (get(walletClient)) { return } try { - const client = await Web3Wallet.init({ - core: new Core({ - projectId: process.env.WALLETCONNECT_PROJECT_ID ?? '41511f9b50c46a80cdf8bd1a3532f2f9', - }), + const _walletClient = await Web3Wallet.init({ + core: WALLET_CONNECT_CORE, metadata: WALLET_METADATA, }) - walletClient.set(client) + walletClient.set(_walletClient) setConnectedDapps() - client.on('session_proposal', (sessionProposal) => void onSessionProposal(sessionProposal)) - client.on('session_request', (event) => onSessionRequest(event)) - client.on('session_delete', (event) => onSessionDelete(event)) + _walletClient.on('session_proposal', (sessionProposal) => void onSessionProposal(sessionProposal)) + _walletClient.on('session_request', (event) => onSessionRequest(event)) + _walletClient.on('session_delete', (event) => onSessionDelete(event)) } catch (err) { handleError(err) } diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/constants/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/constants/index.ts index ea6c8a12f5..ae121cfd7f 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/constants/index.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/constants/index.ts @@ -2,4 +2,5 @@ export * from './all-supported-methods.constant' export * from './general-supported-methods.constant' export * from './methods-for-permissions.constant' export * from './supported-events.constant' +export * from './wallet-connect-core.constant' export * from './wallet-metadata.constant' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/constants/wallet-connect-core.constant.ts b/packages/shared/src/lib/auxiliary/wallet-connect/constants/wallet-connect-core.constant.ts new file mode 100644 index 0000000000..a096ebfa9a --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/constants/wallet-connect-core.constant.ts @@ -0,0 +1,5 @@ +import { Core } from '@walletconnect/core' + +export const WALLET_CONNECT_CORE = new Core({ + projectId: process.env.WALLETCONNECT_PROJECT_ID, +}) diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/actions/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/actions/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/classes/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/classes/index.ts new file mode 100644 index 0000000000..f2305ba246 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/classes/index.ts @@ -0,0 +1 @@ +export * from './notificationsManager.class' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/classes/notificationsManager.class.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/classes/notificationsManager.class.ts new file mode 100644 index 0000000000..e9664fbcca --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/classes/notificationsManager.class.ts @@ -0,0 +1,356 @@ +import { getCaip10AddressForAccount } from '../../utils' +import { IAccountState } from '@core/account' +import { EvmNetworkId, SupportedIscNetworkId } from '@core/network' +import { signMessage } from '@core/wallet' +import { NotifyClient, NotifyClientTypes } from '@walletconnect/notify-client' +import { Writable, get, writable } from 'svelte/store' +import { NotifyEvent } from '../enums' +import { WALLET_CONNECT_CORE } from '../../constants/wallet-connect-core.constant' +import { PartialWithId } from '@core/utils' + +// TODO: where should this be placed? +const APP_DOMAIN = 'bloomwallet.io' +const APP_URL = 'https://' + APP_DOMAIN + +export type Notifications = { [topic: string]: NotifyClientTypes.NotifyNotification[] } +export type Subscriptions = { [topic: string]: NotifyClientTypes.NotifySubscription } +export class NotificationsManager { + private notifyClient: NotifyClient | undefined + public trackedNetworkAddresses: Writable> = writable(new Set()) + public subscriptionsPerAddress: Writable<{ [address: string]: Subscriptions }> = writable({}) + public notificationsPerSubscription: Writable = writable({}) + + constructor() {} + + async init(): Promise { + try { + this.notifyClient = await NotifyClient.init({ + core: WALLET_CONNECT_CORE, + }) + + this.initialiseHandlers() + } catch (err) { + console.error('Unable to initialise Notify Client', err) + } + } + + private initialiseHandlers(): void { + if (!this.notifyClient) { + return + } + + this.notifyClient.on( + NotifyEvent.SubscriptionsChanged, + (event: NotifyClientTypes.EventArguments[NotifyEvent.SubscriptionsChanged]) => { + const subscriptions = event.params.subscriptions.filter((subscription) => + get(this.trackedNetworkAddresses).has(subscription.account) + ) + + this.updateSubscriptionsPerAddress(subscriptions) + void this.updateNotificationsForSubscriptions(subscriptions) + } + ) + + // There is both a notification and a message event that returns at the same time, + // We have only registered a listener for notification and will ask WC what the difference is + this.notifyClient.on( + NotifyEvent.Notification, + (event: NotifyClientTypes.EventArguments[NotifyEvent.Notification]) => { + // The types are not correct on this event, so we need to cast it to the correct type + this.handleNewNotification( + event.topic, + event.params.notification as unknown as NotifyClientTypes.NotifyServerNotification + ) + } + ) + + this.notifyClient.on(NotifyEvent.Update, (event: NotifyClientTypes.EventArguments[NotifyEvent.Update]) => { + console.warn('Unimplemented: NotifyEvent.Update', event) + }) + + this.notifyClient.on(NotifyEvent.Delete, (event: NotifyClientTypes.EventArguments[NotifyEvent.Delete]) => { + console.warn('Unimplemented: NotifyEvent.Delete', event) + }) + + this.notifyClient.on( + NotifyEvent.Subscription, + (_event: NotifyClientTypes.EventArguments[NotifyEvent.Subscription]) => { + // Unable to do this as expected because the subscription we receive back here is the wrong one... + /* const newSubscription = event.params.subscription + if (newSubscription && get(this.trackedNetworkAddresses)?.has(newSubscription?.account)) { + this.updateSubscriptionsPerAddress([newSubscription]) + void this.updateNotificationsForSubscriptions([newSubscription]) + } */ + } + ) + } + + private static buildNotifyClientOptions( + account: IAccountState, + networkId: EvmNetworkId + ): + | { + account: string + domain: string + allApps: boolean + } + | undefined { + const caip10Address = getCaip10AddressForAccount(account, networkId) + if (!caip10Address) { + return undefined + } + + return { + account: caip10Address, + domain: APP_URL, + allApps: true, + } + } + + isRegistered(account: IAccountState, networkId: EvmNetworkId): boolean { + const notifyClientOptions = NotificationsManager.buildNotifyClientOptions(account, networkId) + if (!notifyClientOptions) { + return false + } + + return this.notifyClient?.isRegistered(notifyClientOptions) || false + } + + async registerAccount(account: IAccountState, networkId: EvmNetworkId, coinType: number): Promise { + if (!this.notifyClient) { + return + } + + const notifyClientOptions = NotificationsManager.buildNotifyClientOptions(account, networkId) + if (!notifyClientOptions) { + return + } + + if (this.notifyClient.isRegistered(notifyClientOptions)) { + return + } + + const { registerParams, message } = await this.notifyClient.prepareRegistration(notifyClientOptions) + const signature = await signMessage(message, coinType, account) + if (!signature) { + return + } + + await this.notifyClient.register({ + registerParams, + signature, + }) + + this.addTrackedNetworkAccounts([account], networkId) + + if (Object.values(SupportedIscNetworkId).includes(networkId)) { + await this.subscribeToBloom(account, networkId) + } + } + + async unregisterAccount(account: IAccountState, networkId: EvmNetworkId): Promise { + if (!this.notifyClient) { + return + } + + const caip10Address = getCaip10AddressForAccount(account, networkId) + if (!caip10Address) { + return + } + + await this.notifyClient.unregister({ account: caip10Address }) + this.removeTrackedNetworkAccounts(caip10Address) + } + + async setTrackedNetworkAccounts(accounts: IAccountState[], networkIds: EvmNetworkId[]): Promise { + this.trackedNetworkAddresses.set(new Set()) + for (const networkId of networkIds) { + this.addTrackedNetworkAccounts(accounts, networkId) + } + await this.refreshAllSubscriptionsAndNotificationsForTrackedAccounts() + } + + getSubscriptionsForTopic(topic: string): NotifyClientTypes.NotifySubscription | undefined { + return Object.values(get(this.subscriptionsPerAddress)) + .map((subscriptions) => subscriptions[topic]) + .find(Boolean) + } + + addTrackedNetworkAccounts(accounts: IAccountState[], networkId: EvmNetworkId): void { + const newNetworkAddressesToTrack = new Set( + accounts + .filter((account) => this.isRegistered(account, networkId)) + .map((acc) => getCaip10AddressForAccount(acc, networkId)) + .filter(Boolean) as string[] + ) + + // TODO: Upgrade to this set operation once we upgade node to v22+ + this.trackedNetworkAddresses.update((state) => { + newNetworkAddressesToTrack.forEach((address) => state.add(address)) + return state + }) + } + + async refreshAllSubscriptionsAndNotificationsForTrackedAccounts(): Promise { + this.subscriptionsPerAddress.set({}) + this.notificationsPerSubscription.set({}) + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/union#browser_compatibility + // this.trackedNetworkAddresses = this.trackedNetworkAddresses.union(newNetworkAddressesToTrack) + for (const networkAddress of get(this.trackedNetworkAddresses)) { + await this.updateSubscriptionsAndNotificationsForNetworkAddress(networkAddress) + } + } + + removeTrackedNetworkAccounts(networkAddress: string): void { + this.trackedNetworkAddresses.update((state) => { + state.delete(networkAddress) + return state + }) + + let subscriptionTopics: string[] = [] + this.subscriptionsPerAddress.update((state) => { + subscriptionTopics = Object.keys(state[networkAddress] ?? {}) + state[networkAddress] = {} + return state + }) + + this.notificationsPerSubscription.update((state) => { + for (const subscriptionTopic of subscriptionTopics) { + delete state[subscriptionTopic] + } + return state + }) + } + + clearTrackedNetworkAccounts(): void { + this.trackedNetworkAddresses.set(new Set()) + this.subscriptionsPerAddress.set({}) + this.notificationsPerSubscription.set({}) + } + + private async updateSubscriptionsAndNotificationsForNetworkAddress(networkAddress: string): Promise { + if (!this.notifyClient) { + return + } + + const activeSubscriptions = Object.values(this.notifyClient.getActiveSubscriptions({ account: networkAddress })) + this.updateSubscriptionsPerAddress(activeSubscriptions) + + try { + await this.updateNotificationsForSubscriptions(activeSubscriptions) + } catch (error) { + console.error(error) + } + } + + private updateSubscriptionsPerAddress(newSubscriptions: NotifyClientTypes.NotifySubscription[]): void { + this.subscriptionsPerAddress.update((state) => { + for (const subscription of newSubscriptions) { + const subscriptionsForAccount = state[subscription.account] ?? {} + subscriptionsForAccount[subscription.topic] = subscription + state[subscription.account] = subscriptionsForAccount + } + return state + }) + } + + private async updateNotificationsForSubscriptions( + subscriptions: NotifyClientTypes.NotifySubscription[] + ): Promise { + if (!this.notifyClient) { + return + } + + const notificationsPerSubscription: Notifications = {} + for (const subscription of subscriptions) { + const notificationHistory = await this.notifyClient.getNotificationHistory({ topic: subscription.topic }) + notificationsPerSubscription[subscription.topic] = notificationHistory.notifications + } + + this.notificationsPerSubscription.update((state) => { + return { ...state, ...notificationsPerSubscription } + }) + } + + private updateNotificationForSubscription( + partialNotification: PartialWithId, + subscriptionTopic: string + ): void { + this.notificationsPerSubscription.update((state) => { + const notificationsForTopic = state[subscriptionTopic] + if (!notificationsForTopic) { + return state + } + + const updatedNotifications = notificationsForTopic.map((notification) => { + if (notification.id === partialNotification.id) { + return { ...notification, ...partialNotification } + } + return notification + }) + + state[subscriptionTopic] = updatedNotifications + return state + }) + } + + private handleNewNotification(topic: string, notification: NotifyClientTypes.NotifyServerNotification): void { + const notificationsForTopic = get(this.notificationsPerSubscription)[topic] + + if (!notificationsForTopic || notificationsForTopic?.find((n) => n.id === notification.id)) { + return + } + + this.notificationsPerSubscription.update((state) => { + const parsedNotification = { + title: notification.title, + sentAt: notification.sent_at, + body: notification.body, + id: notification.id, + url: notification.url, + isRead: notification.is_read, + type: notification.type, + } + + state[topic] = [...state[topic], parsedNotification] + return state + }) + } + + async subscribeToDapp(appDomain: string, account: IAccountState, networkId: EvmNetworkId): Promise { + if (!this.notifyClient) { + return false + } + + const caip10Address = getCaip10AddressForAccount(account, networkId) + if (!caip10Address) { + return false + } + + return await this.notifyClient.subscribe({ appDomain, account: caip10Address }) + } + + async subscribeToBloom(account: IAccountState, networkId: EvmNetworkId): Promise { + try { + await this.subscribeToDapp(APP_DOMAIN, account, networkId) + void this.refreshAllSubscriptionsAndNotificationsForTrackedAccounts() + } catch (error) { + console.error('Error subscribing to dapp', error) + } + } + + async markAsRead(notificationId: string, subscriptionTopic: string): Promise { + try { + await this.notifyClient?.markNotificationsAsRead({ + notificationIds: [notificationId], + topic: subscriptionTopic, + }) + this.updateNotificationForSubscription({ id: notificationId, isRead: true }, subscriptionTopic) + } catch (e) { + console.error('Error marking notifications as read', e) + } + } +} + +export const notificationsManager = new NotificationsManager() diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/enums/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/enums/index.ts new file mode 100644 index 0000000000..52c4491415 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/enums/index.ts @@ -0,0 +1 @@ +export * from './notify-event.enum' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/enums/notify-event.enum.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/enums/notify-event.enum.ts new file mode 100644 index 0000000000..13adcaf0e9 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/enums/notify-event.enum.ts @@ -0,0 +1,8 @@ +export enum NotifyEvent { + Subscription = 'notify_subscription', + Message = 'notify_message', + Notification = 'notify_notification', + Delete = 'notify_delete', + Update = 'notify_update', + SubscriptionsChanged = 'notify_subscriptions_changed', +} diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/index.ts new file mode 100644 index 0000000000..f50cdd743a --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/index.ts @@ -0,0 +1 @@ +export * from './classes' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/stores/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/stores/index.ts new file mode 100644 index 0000000000..24a4c7d124 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/stores/index.ts @@ -0,0 +1 @@ +export * from './notify-client.store' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/notifications/stores/notify-client.store.ts b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/stores/notify-client.store.ts new file mode 100644 index 0000000000..3274689c3b --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/notifications/stores/notify-client.store.ts @@ -0,0 +1,8 @@ +import { NotifyClient } from '@walletconnect/notify-client/dist/types/client' +import { Writable, get, writable } from 'svelte/store' + +export const notifyClient: Writable = writable(undefined) + +export function getNotifyClient(): NotifyClient | undefined { + return get(notifyClient) +} diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/stores/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/stores/index.ts index cac401ea14..6e8637ec7f 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/stores/index.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/stores/index.ts @@ -1,6 +1,6 @@ export * from './connected-dapps.store' -export * from './wallet-client.store' export * from './persisted-dapps.store' export * from './persisted-dapp-namespaces.store' export * from './selected-dapp.store' export * from './session-proposal.store' +export * from './wallet-client.store' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/types/caip10-address.type.ts b/packages/shared/src/lib/auxiliary/wallet-connect/types/caip10-address.type.ts new file mode 100644 index 0000000000..c2b45bba21 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/types/caip10-address.type.ts @@ -0,0 +1,3 @@ +import { EvmNetworkId } from '@core/network' + +export type Caip10Address = `${EvmNetworkId}:${string}` diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/types/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/types/index.ts index 40df081e43..69e390f814 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/types/index.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/types/index.ts @@ -1,2 +1,3 @@ +export * from './caip10-address.type' export * from './callback-parameters.type' export * from './supported-namespaces.type' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/utils/buildCaip10Address.ts b/packages/shared/src/lib/auxiliary/wallet-connect/utils/buildCaip10Address.ts new file mode 100644 index 0000000000..47b2d4e3d9 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/utils/buildCaip10Address.ts @@ -0,0 +1,16 @@ +import { EvmNetworkId } from '@core/network' +import { toChecksumAddress } from '@ethereumjs/util' +import { Caip10Address } from '../types' +import { IAccountState, getAddressFromAccountForNetwork } from '@core/account' + +export function buildCaip10Address(address: string, evmNetworkId: EvmNetworkId): Caip10Address { + return `${evmNetworkId}:${toChecksumAddress(address)}` +} + +export function getCaip10AddressForAccount( + account: IAccountState, + evmNetworkId: EvmNetworkId +): Caip10Address | undefined { + const accountAddress = getAddressFromAccountForNetwork(account, evmNetworkId) + return accountAddress ? buildCaip10Address(accountAddress, evmNetworkId) : undefined +} diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/utils/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/utils/index.ts index 814cd938da..197edb26cf 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/utils/index.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/utils/index.ts @@ -1,3 +1,4 @@ +export * from './buildCaip10Address' export * from './disconnectAllDapps' export * from './doesNamespaceSupportEvent' export * from './getBloomError' diff --git a/packages/shared/src/lib/contexts/onboarding/actions/completeOnboardingProcess.ts b/packages/shared/src/lib/contexts/onboarding/actions/completeOnboardingProcess.ts index 2e5e945889..93ed58dfe4 100644 --- a/packages/shared/src/lib/contexts/onboarding/actions/completeOnboardingProcess.ts +++ b/packages/shared/src/lib/contexts/onboarding/actions/completeOnboardingProcess.ts @@ -9,6 +9,7 @@ import { OnboardingType } from '../enums' import { onboardingProfile } from '../stores' import { cleanupOnboarding } from './cleanupOnboarding' import { createNewProfileFromOnboardingProfile } from './createNewProfileFromOnboardingProfile' +import { notificationsManager } from '@auxiliary/wallet-connect/notifications' export async function completeOnboardingProcess(): Promise { // if we already have an active profile @@ -31,15 +32,14 @@ export async function completeOnboardingProcess(): Promise { if (type === ProfileType.Software) { const { network, evmNetworks } = get(activeProfile) - const coinTypes = new Set( - [ - ...network.chainConfigurations.map((chain) => chain.coinType), - ...(evmNetworks ?? []).map((evmNetwork) => evmNetwork.coinType), - ].filter((cointype) => cointype !== undefined) - ) - - for (const coinType of coinTypes) { - await generateAndStoreEvmAddressForAccounts(type, coinType, ...accounts) + const networks = [...network.chainConfigurations, ...(evmNetworks ?? [])] + for (const network of networks) { + await generateAndStoreEvmAddressForAccounts(type, network.coinType, ...accounts) + try { + await notificationsManager.registerAccount(accounts[0], network.id, network.coinType) + } catch (error) { + console.error(error) + } } } diff --git a/packages/shared/src/lib/core/account/utils/getAddressFromAccountForNetwork.ts b/packages/shared/src/lib/core/account/utils/getAddressFromAccountForNetwork.ts index 712b8ff73c..603577f555 100644 --- a/packages/shared/src/lib/core/account/utils/getAddressFromAccountForNetwork.ts +++ b/packages/shared/src/lib/core/account/utils/getAddressFromAccountForNetwork.ts @@ -1,11 +1,11 @@ -import { NetworkId, getEvmNetwork, isStardustNetwork } from '@core/network' +import { DEFAULT_COIN_TYPE, ETHEREUM_COIN_TYPE, NetworkId, getEvmNetwork, isStardustNetwork } from '@core/network' import { IAccountState } from '../interfaces' export function getAddressFromAccountForNetwork(account: IAccountState, networkId: NetworkId): string | undefined { if (isStardustNetwork(networkId)) { return account.depositAddress } else { - const coinType = getEvmNetwork(networkId)?.coinType + const coinType = getEvmNetwork(networkId)?.coinType ?? DEFAULT_COIN_TYPE[networkId] ?? ETHEREUM_COIN_TYPE return coinType !== undefined ? account.evmAddresses[coinType] : undefined } } diff --git a/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts b/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts index 83a3f6c0f2..b3d2d0df49 100644 --- a/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts +++ b/packages/shared/src/lib/core/app/constants/external-allowed-links.constant.ts @@ -29,6 +29,9 @@ export const externalAllowedLinks = [ // Discord Invite 'discord.gg/RjX3jEc7K7', + // Wallet Connect + 'notify.walletconnect.com/v1/notification/*', + // Other 'support.ledger.com', 'medium.com', diff --git a/packages/shared/src/lib/core/app/utils/openUrlInBrowser.ts b/packages/shared/src/lib/core/app/utils/openUrlInBrowser.ts index f42b572673..b35e3bae37 100644 --- a/packages/shared/src/lib/core/app/utils/openUrlInBrowser.ts +++ b/packages/shared/src/lib/core/app/utils/openUrlInBrowser.ts @@ -21,7 +21,14 @@ export function openUrlInBrowser(targetUrl: string | undefined): void { const url = new URL(targetUrl) const domain = url.hostname.replace(/^www\./, '') - const isAllowed = externalAllowedLinks.includes(domain) || externalAllowedLinks.includes(domain + url.pathname) + const isAllowed = externalAllowedLinks.some((link) => { + if (link.endsWith('*')) { + const prefix = link.slice(0, -1) + return domain.startsWith(prefix) || (domain + url.pathname).startsWith(prefix) + } else { + return link === domain || link === domain + url.pathname + } + }) if (isAllowed) { openHttpsUrlsOnly(url.protocol, targetUrl) diff --git a/packages/shared/src/lib/core/layer-2/actions/generateAndStoreEvmAddressForAccounts.ts b/packages/shared/src/lib/core/layer-2/actions/generateAndStoreEvmAddressForAccounts.ts index 53ee39c1d9..ec2fb5f27b 100644 --- a/packages/shared/src/lib/core/layer-2/actions/generateAndStoreEvmAddressForAccounts.ts +++ b/packages/shared/src/lib/core/layer-2/actions/generateAndStoreEvmAddressForAccounts.ts @@ -46,6 +46,7 @@ export async function generateAndStoreEvmAddressForAccounts( } } + account.evmAddresses[coinType] = evmAddress const evmAddresses = { [coinType]: evmAddress } updateActiveAccount(accountIndex, { evmAddresses }) updateActiveAccountPersistedData(accountIndex, { evmAddresses }) diff --git a/packages/shared/src/lib/core/layer-2/utils/validateSiwe.ts b/packages/shared/src/lib/core/layer-2/utils/validateSiwe.ts index 015af9f806..4d742ae833 100644 --- a/packages/shared/src/lib/core/layer-2/utils/validateSiwe.ts +++ b/packages/shared/src/lib/core/layer-2/utils/validateSiwe.ts @@ -1,9 +1,17 @@ import { ParsedMessage } from '@spruceid/siwe-parser' export function validateSiwe(siweObject: ParsedMessage, origin: string | undefined): boolean { - if (siweObject.domain !== origin && `https://${siweObject.domain}` !== origin) { - return false + // if localhost is the domain of the dapp, allow it + const potentialDomains = [ + siweObject.domain, + `https://${siweObject.domain}`, + `https://${siweObject.domain}/`, + `${siweObject.domain}/`, + ] + + if (origin && potentialDomains.includes(origin.toString())) { + return true } - return true + return false } diff --git a/packages/shared/src/lib/core/profile/actions/active-profile/login.ts b/packages/shared/src/lib/core/profile/actions/active-profile/login.ts index 2b017354e0..4e9668b546 100644 --- a/packages/shared/src/lib/core/profile/actions/active-profile/login.ts +++ b/packages/shared/src/lib/core/profile/actions/active-profile/login.ts @@ -43,6 +43,8 @@ import { initializeWalletConnect } from '@auxiliary/wallet-connect/actions' import { cleanupOnboarding } from '@contexts/onboarding' import { fetchAndPersistTransactionsForAccounts } from '@core/transactions/actions' import { updateCirculatingSupplyForActiveProfile } from './updateCirculatingSupplyForActiveProfile' +import { notificationsManager } from '@auxiliary/wallet-connect/notifications' +import { getEvmNetworks } from '@core/network' export async function login(loginOptions?: ILoginOptions): Promise { const loginRouter = get(routerManager)?.getRouterForAppContext(AppContext.Login) @@ -133,6 +135,10 @@ export async function login(loginOptions?: ILoginOptions): Promise { } void cleanupOnboarding() void initializeWalletConnect() + notificationsManager.setTrackedNetworkAccounts( + loadedAccounts, + getEvmNetworks().map(({ id }) => id) + ) } catch (err) { handleError(err) if (!loginOptions?.isFromOnboardingFlow) { diff --git a/packages/shared/src/lib/core/profile/actions/active-profile/logout.ts b/packages/shared/src/lib/core/profile/actions/active-profile/logout.ts index f9f7ce74f7..cb2037cef5 100644 --- a/packages/shared/src/lib/core/profile/actions/active-profile/logout.ts +++ b/packages/shared/src/lib/core/profile/actions/active-profile/logout.ts @@ -26,6 +26,7 @@ import { clearActiveProfileNftsPerAccount } from '@core/nfts/stores' import { clearAccountActivities } from '@core/activity/stores' import { destroyNetworks } from '@core/network/stores' import { resetClient } from '@core/profile-manager/api' +import { notificationsManager } from '@auxiliary/wallet-connect/notifications/classes' /** * Logout from active profile @@ -67,6 +68,8 @@ function cleanupProfileState(clearActiveProfile: boolean): void { void stopDownloadingNftMediaFromQueue() + notificationsManager.clearTrackedNetworkAccounts() + // Governance Stores resetRegisteredProposals() resetProposalOverviews() diff --git a/packages/shared/src/lib/core/utils/tests/time.test.ts b/packages/shared/src/lib/core/utils/tests/time.test.ts index 65a8a68a99..6abce6b1e6 100644 --- a/packages/shared/src/lib/core/utils/tests/time.test.ts +++ b/packages/shared/src/lib/core/utils/tests/time.test.ts @@ -52,6 +52,13 @@ describe('File: time.ts', () => { expect(getBestTimeDuration(MILLISECONDS_PER_SECOND * 30)).toEqual('30 seconds') expect(getBestTimeDuration(MILLISECONDS_PER_SECOND)).toEqual('1 second') }) + + it('should return best duration with minimal param', () => { + expect(getBestTimeDuration(ONE_DAY_MILLIS * 2, 'day', true)).toEqual('2d') + expect(getBestTimeDuration(ONE_MINUTE_MILLIS * 60, 'day', true)).toEqual('1h') + expect(getBestTimeDuration(ONE_MINUTE_MILLIS * 30, 'day', true)).toEqual('30m') + expect(getBestTimeDuration(MILLISECONDS_PER_SECOND, 'day', true)).toEqual('1s') + }) }) describe('Function: isFutureDateTime', () => { diff --git a/packages/shared/src/lib/core/utils/time.ts b/packages/shared/src/lib/core/utils/time.ts index 5a91e8c5cf..be225b52b4 100644 --- a/packages/shared/src/lib/core/utils/time.ts +++ b/packages/shared/src/lib/core/utils/time.ts @@ -36,24 +36,34 @@ export function isFutureDateTime(dateTime: Date): boolean { * * @returns {string} */ -export const getBestTimeDuration = (millis: number, noDurationUnit: Duration = 'day'): string => { - const zeroTime = localize(`times.${noDurationUnit || 'day'}`, { values: { time: 0 } }) +export const getBestTimeDuration = (millis: number, noDurationUnit: Duration = 'day', minimal = false): string => { + const zeroTime = minimal ? '0' : localize(`times.${noDurationUnit || 'day'}`, { values: { time: 0 } }) if (Number.isNaN(millis)) return zeroTime const inDays = millis / (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND) - if (inDays >= 1) return localize('times.day', { values: { time: inDays > 1 ? Math.ceil(inDays) : inDays } }) + if (inDays >= 1) + return minimal + ? `${Math.ceil(inDays)}d` + : localize('times.day', { values: { time: inDays > 1 ? Math.ceil(inDays) : inDays } }) const inHours = millis / (MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND) - if (inHours >= 1) return localize('times.hour', { values: { time: inHours > 1 ? Math.ceil(inHours) : inHours } }) + if (inHours >= 1) + return minimal + ? `${Math.ceil(inHours)}h` + : localize('times.hour', { values: { time: inHours > 1 ? Math.ceil(inHours) : inHours } }) const inMinutes = millis / (SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND) if (inMinutes >= 1) - return localize('times.minute', { values: { time: inMinutes > 1 ? Math.ceil(inMinutes) : inMinutes } }) + return minimal + ? `${Math.ceil(inMinutes)}m` + : localize('times.minute', { values: { time: inMinutes > 1 ? Math.ceil(inMinutes) : inMinutes } }) const inSeconds = millis / MILLISECONDS_PER_SECOND if (inSeconds >= 1) - return localize('times.second', { values: { time: inSeconds > 1 ? Math.ceil(inSeconds) : inSeconds } }) + return minimal + ? `${Math.ceil(inSeconds)}s` + : localize('times.second', { values: { time: inSeconds > 1 ? Math.ceil(inSeconds) : inSeconds } }) return zeroTime } diff --git a/packages/shared/src/lib/features/interfaces/wallet-connect-features.interface.ts b/packages/shared/src/lib/features/interfaces/wallet-connect-features.interface.ts index 55e1e211d3..314ac62164 100644 --- a/packages/shared/src/lib/features/interfaces/wallet-connect-features.interface.ts +++ b/packages/shared/src/lib/features/interfaces/wallet-connect-features.interface.ts @@ -1,3 +1,6 @@ import { IFeatureFlag } from './feature-flag.interface' -export interface IWalletConnectFeatures extends IFeatureFlag {} +export interface IWalletConnectFeatures extends IFeatureFlag { + web3Wallet: IFeatureFlag + notifications: IFeatureFlag +} diff --git a/packages/shared/src/locales/en.json b/packages/shared/src/locales/en.json index 88dee42bac..f177bdcea1 100644 --- a/packages/shared/src/locales/en.json +++ b/packages/shared/src/locales/en.json @@ -652,6 +652,13 @@ "body": "A new Bloom update is available today.", "button": "Update now" } + }, + "dappNotifications": { + "title": "Notifications", + "notEnabledHint": "Receiving notifications not enabled", + "empty": "No notifications", + "enable": "Enable notifications", + "viewAll": "View all notifications" } }, "picker": { @@ -1497,7 +1504,8 @@ "readMore": "Read more", "importThirdPartyProfiles": "Import third-party profiles", "retry": "Retry", - "loadAnyway": "Load anyway" + "loadAnyway": "Load anyway", + "visit": "Visit" }, "general": { "recipient": "Recipient", @@ -1694,6 +1702,8 @@ "maxFees": "Max fees", "contacts": "Contacts", "apps": "Apps", + "connectedDapps": "Connected dApps", + "dapps": "dApps", "networks": "Networks", "ledgerDevice": "Ledger device", "disconnected": "Disconnected", @@ -1739,7 +1749,10 @@ "fast": "Fast", "spender": "Spender", "status": "Status", - "totalBalance": "Total balance" + "totalBalance": "Total balance", + "saving": "Saving", + "enabled": "Enabled", + "disabled": "Disabled" }, "filters":{ "title": "Filters", diff --git a/yarn.lock b/yarn.lock index a3a063495c..30ff99aa5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -343,10 +343,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bloomwalletio/ui@0.21.3": - version "0.21.3" - resolved "https://npm.pkg.github.com/download/@bloomwalletio/ui/0.21.3/2bf5587205e01b89f67cd6b990495d3a1175b5de#2bf5587205e01b89f67cd6b990495d3a1175b5de" - integrity sha512-McwM3nbGmGkkq8zJAWBfriMvSDXXurTrqL26vHrTbNP1LNJJSujRo4o2Oep0sjf1xeQRoHy7HZSPVml/EKwF/g== +"@bloomwalletio/ui@0.21.5": + version "0.21.5" + resolved "https://npm.pkg.github.com/download/@bloomwalletio/ui/0.21.5/cfc5ca711be3fc777901c96fb4b65fd9c5e490bf#cfc5ca711be3fc777901c96fb4b65fd9c5e490bf" + integrity sha512-HqFoiP8BIJ6cuDnvzV8bRxx3usOGQ+oBn47VCN+7IaHcEn1LIshWwqEamzshdGL+jofadv7CyHri8ZVwbeZHIg== dependencies: "@floating-ui/dom" "1.4.3" "@popperjs/core" "2.11.8" @@ -1848,6 +1848,11 @@ dependencies: "@noble/hashes" "1.3.3" +"@noble/ed25519@1.7.3", "@noble/ed25519@^1.7.1": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123" + integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ== + "@noble/hashes@1.3.3", "@noble/hashes@^1.1.2", "@noble/hashes@^1.3.1", "@noble/hashes@~1.3.2": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" @@ -2776,6 +2781,15 @@ events "^3.3.0" isomorphic-unfetch "^3.1.0" +"@walletconnect/cacao@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@walletconnect/cacao/-/cacao-1.0.2.tgz#a5b36db21047503d2cfa0b5466aed30fc02a2840" + integrity sha512-LEO/MqbVvaOj0BerlBJQMsyEfSZGGUusHkICkVH1tIxrk7QCBJ5SCU0XzbVkncXMHqNPvyThEjEFSvf+vdYFJA== + dependencies: + "@ethersproject/hash" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + isomorphic-unfetch "^3.1.0" + "@walletconnect/core@2.12.2", "@walletconnect/core@^2.10.1": version "2.12.2" resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.12.2.tgz#12bd568b90daed876e58ebcc098c12843a3321e6" @@ -2799,6 +2813,24 @@ lodash.isequal "4.5.0" uint8arrays "^3.1.0" +"@walletconnect/did-jwt@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@walletconnect/did-jwt/-/did-jwt-2.0.1.tgz#7133f3faa6396e86a3c7e758bd2ddf13c700e269" + integrity sha512-wu6Mth4pV3kgE2WfSSyoZoRS0B4Kp1TLSVmD22NhfosVDFWSan0lF7wIqcPVTV7lGm/wTiOvJvTT7HfQtKXwGw== + dependencies: + "@noble/ed25519" "1.7.3" + bs58 "5.0.0" + multiformats "9.9.0" + +"@walletconnect/did-jwt@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@walletconnect/did-jwt/-/did-jwt-2.1.0.tgz#a5a3f2feb652bd3c4c98aee25b88f071309c3964" + integrity sha512-4K1uZpiFS/wk4zSbB2PWGdQPtVnM3DQokyAVLJSHDg+91vF47qG0jdlr2WRTgXasXcZbRhVRHWLdhWbzznt1dA== + dependencies: + "@noble/ed25519" "1.7.3" + bs58 "5.0.0" + multiformats "^9.9.0" + "@walletconnect/environment@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@walletconnect/environment/-/environment-1.0.1.tgz#1d7f82f0009ab821a2ba5ad5e5a7b8ae3b214cd7" @@ -2806,7 +2838,7 @@ dependencies: tslib "1.14.1" -"@walletconnect/events@^1.0.1": +"@walletconnect/events@1.0.1", "@walletconnect/events@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@walletconnect/events/-/events-1.0.1.tgz#2b5f9c7202019e229d7ccae1369a9e86bda7816c" integrity sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ== @@ -2823,6 +2855,30 @@ "@walletconnect/time" "^1.0.2" tslib "1.14.1" +"@walletconnect/heartbeat@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@walletconnect/heartbeat/-/heartbeat-1.2.2.tgz#e8dc5179db7769950c6f9cf59b23516d9b95227d" + integrity sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw== + dependencies: + "@walletconnect/events" "^1.0.1" + "@walletconnect/time" "^1.0.2" + events "^3.3.0" + +"@walletconnect/identity-keys@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@walletconnect/identity-keys/-/identity-keys-2.1.0.tgz#a4673f12b16879f0a4d3f9fea874ead09bac3cb3" + integrity sha512-cBW0afKS+JCBLypfmuSuODrhTinKveeKL2FRIkHfBDS12zaqONS4HR+EZAFNo9gXA0P2yei8BG2r581VbTwHJw== + dependencies: + "@ethersproject/hash" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@noble/ed25519" "^1.7.1" + "@walletconnect/cacao" "1.0.2" + "@walletconnect/core" "^2.10.1" + "@walletconnect/did-jwt" "2.1.0" + "@walletconnect/types" "^2.10.1" + "@walletconnect/utils" "^2.10.1" + axios "^1.3.5" + "@walletconnect/jsonrpc-provider@1.0.13": version "1.0.13" resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.13.tgz#9a74da648d015e1fffc745f0c7d629457f53648b" @@ -2848,6 +2904,15 @@ events "^3.3.0" keyvaluestorage-interface "^1.0.0" +"@walletconnect/jsonrpc-utils@1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.7.tgz#1812d17c784f1ec0735bf03d0884287f60bfa2ce" + integrity sha512-zJziApzUF/Il4VcwabnaU+0yo1QI4eUkYX99zmCVTHJvZOf2l0zjADf/OpKqWyeNFC3Io56Z/8uJHVtcNVvyFA== + dependencies: + "@walletconnect/environment" "^1.0.1" + "@walletconnect/jsonrpc-types" "^1.0.2" + tslib "1.14.1" + "@walletconnect/jsonrpc-utils@1.0.8", "@walletconnect/jsonrpc-utils@^1.0.6", "@walletconnect/jsonrpc-utils@^1.0.8": version "1.0.8" resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz#82d0cc6a5d6ff0ecc277cb35f71402c91ad48d72" @@ -2867,7 +2932,7 @@ events "^3.3.0" ws "^7.5.1" -"@walletconnect/keyvaluestorage@^1.1.1": +"@walletconnect/keyvaluestorage@1.1.1", "@walletconnect/keyvaluestorage@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz#dd2caddabfbaf80f6b8993a0704d8b83115a1842" integrity sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA== @@ -2884,6 +2949,22 @@ "@walletconnect/safe-json" "^1.0.2" pino "7.11.0" +"@walletconnect/notify-client@1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@walletconnect/notify-client/-/notify-client-1.4.1.tgz#a79da37b0af519239f110b6787f12d5913ed16d4" + integrity sha512-+1ZwPjaleDYJpoeqAGsBRPv385D+RyXvIbt+0pMiezZDxg2zGfMOu4FBA1VkQmQFQqjzzImTA9a1/LxtbWF4Og== + dependencies: + "@noble/ed25519" "1.7.3" + "@walletconnect/cacao" "1.0.2" + "@walletconnect/core" "2.12.2" + "@walletconnect/did-jwt" "2.0.1" + "@walletconnect/identity-keys" "2.1.0" + "@walletconnect/jsonrpc-utils" "1.0.7" + "@walletconnect/time" "1.0.2" + "@walletconnect/utils" "2.12.2" + axios "1.4.0" + jwt-decode "3.1.2" + "@walletconnect/relay-api@^1.0.9": version "1.0.9" resolved "https://registry.yarnpkg.com/@walletconnect/relay-api/-/relay-api-1.0.9.tgz#f8c2c3993dddaa9f33ed42197fc9bfebd790ecaf" @@ -2926,25 +3007,13 @@ "@walletconnect/utils" "2.12.2" events "^3.3.0" -"@walletconnect/time@^1.0.2": +"@walletconnect/time@1.0.2", "@walletconnect/time@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@walletconnect/time/-/time-1.0.2.tgz#6c5888b835750ecb4299d28eecc5e72c6d336523" integrity sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g== dependencies: tslib "1.14.1" -"@walletconnect/types@2.11.3": - version "2.11.3" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.11.3.tgz#8ce43cb77e8fd9d5269847cdd73bcfa7cce7dd1a" - integrity sha512-JY4wA9MVosDW9dcJMTpnwliste0aJGJ1X6Q4ulLsQsgWRSEBRkLila0oUT01TDBW9Yq8uUp7uFOUTaKx6KWVAg== - dependencies: - "@walletconnect/events" "^1.0.1" - "@walletconnect/heartbeat" "1.2.1" - "@walletconnect/jsonrpc-types" "1.0.3" - "@walletconnect/keyvaluestorage" "^1.1.1" - "@walletconnect/logger" "^2.0.1" - events "^3.3.0" - "@walletconnect/types@2.12.2": version "2.12.2" resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.12.2.tgz#8b64a2015a0a96972d28acb2ff317a9a994abfdb" @@ -2957,6 +3026,18 @@ "@walletconnect/logger" "^2.0.1" events "^3.3.0" +"@walletconnect/types@^2.10.1": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.13.0.tgz#cdac083651f5897084fe9ed62779f11810335ac6" + integrity sha512-MWaVT0FkZwzYbD3tvk8F+2qpPlz1LUSWHuqbINUtMXnSzJtXN49Y99fR7FuBhNFtDalfuWsEK17GrNA+KnAsPQ== + dependencies: + "@walletconnect/events" "1.0.1" + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + events "3.3.0" + "@walletconnect/utils@2.12.2", "@walletconnect/utils@^2.10.1": version "2.12.2" resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.12.2.tgz#a2c349d4effef7c1c5e72e74a5483d8dfbb10918" @@ -3567,6 +3648,15 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +axios@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axios@^1.3.4, axios@^1.6.0, axios@^1.6.5: version "1.6.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" @@ -3576,6 +3666,15 @@ axios@^1.3.4, axios@^1.6.0, axios@^1.6.5: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.3.5: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.0.0.tgz#04a4c90dce33cc5d606c76d6216e3b250ff70dab" @@ -3655,6 +3754,11 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -3803,6 +3907,13 @@ browserslist@^4.21.10, browserslist@^4.22.2: node-releases "^2.0.14" update-browserslist-db "^1.0.13" +bs58@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + bs58@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -5490,7 +5601,7 @@ eventemitter3@^5.0.1: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== -events@^3.2.0, events@^3.3.0: +events@3.3.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -5777,7 +5888,7 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -follow-redirects@^1.0.0, follow-redirects@^1.15.4: +follow-redirects@^1.0.0, follow-redirects@^1.15.0, follow-redirects@^1.15.4, follow-redirects@^1.15.6: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== @@ -7432,6 +7543,11 @@ jsonify@^0.0.1: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== +jwt-decode@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + keccak@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" @@ -8058,7 +8174,7 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" -multiformats@^9.4.2: +multiformats@9.9.0, multiformats@^9.4.2, multiformats@^9.9.0: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==