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}
+ (dappImageError = true)}
+ />
+ {/if}
+
+
+ {#if subscription?.metadata.name}
+
+ {/if}
+ {#if notificationType && subscription?.scope[notificationType]}
+
+
+ {#if !notificationImageError}
+ (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]}
+
+ {: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==