diff --git a/.changeset/calm-kangaroos-matter.md b/.changeset/calm-kangaroos-matter.md deleted file mode 100644 index 897d034cc..000000000 --- a/.changeset/calm-kangaroos-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"fuels-wallet": patch ---- - -Fixed setup page not finding some local assets diff --git a/.changeset/dry-flies-add.md b/.changeset/dry-flies-add.md deleted file mode 100644 index 53caf5d98..000000000 --- a/.changeset/dry-flies-add.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"fuels-wallet": patch ---- - -### Changed - -Updated nanoid to the latest versions for better performance and security. \ No newline at end of file diff --git a/.changeset/giant-points-happen.md b/.changeset/giant-points-happen.md deleted file mode 100644 index 22953f347..000000000 --- a/.changeset/giant-points-happen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@fuels/playwright-utils": patch -"fuels-wallet": patch ---- - -Updated Biome to 1.9.4 and fixed lint errors. diff --git a/.changeset/good-candles-perform.md b/.changeset/good-candles-perform.md deleted file mode 100644 index cf8395e7f..000000000 --- a/.changeset/good-candles-perform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"fuels-wallet": patch ---- - -Fixed wallet not loading correctly in local environment on Chrome 130+ diff --git a/.changeset/orange-points-laugh.md b/.changeset/orange-points-laugh.md deleted file mode 100644 index e6f6417e9..000000000 --- a/.changeset/orange-points-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"fuels-wallet": patch ---- - -Fix exception when injecting content in the hello page diff --git a/.changeset/serious-kiwis-kiss.md b/.changeset/serious-kiwis-kiss.md deleted file mode 100644 index e37baa3c3..000000000 --- a/.changeset/serious-kiwis-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"fuels-wallet": patch ---- - -Upgrade Vite to V6 major diff --git a/.changeset/spicy-parents-attend.md b/.changeset/spicy-parents-attend.md deleted file mode 100644 index 6e0aa398e..000000000 --- a/.changeset/spicy-parents-attend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"fuels-wallet": patch ---- - -chore: update 'dev:crx' script to use default env diff --git a/.github/workflows/pr-tests-e2e-crx-lock.yml b/.github/workflows/pr-tests-e2e-crx-lock.yml index 5094bbe2e..9db52bcda 100644 --- a/.github/workflows/pr-tests-e2e-crx-lock.yml +++ b/.github/workflows/pr-tests-e2e-crx-lock.yml @@ -31,13 +31,6 @@ jobs: - name: Generate .env run: cp packages/app/.env.example packages/app/.env - - name: Build Application - run: pnpm build:app - env: - ## increase node.js m memory limit for building - ## with sourcemaps - NODE_OPTIONS: "--max-old-space-size=4096" - - uses: ./.github/actions/setup-playwright - name: Run E2E Tests diff --git a/.github/workflows/pr-tests-e2e.yml b/.github/workflows/pr-tests-e2e.yml index a6eb21757..26eb06eb3 100644 --- a/.github/workflows/pr-tests-e2e.yml +++ b/.github/workflows/pr-tests-e2e.yml @@ -31,13 +31,6 @@ jobs: - name: Generate .env run: cp packages/app/.env.example packages/app/.env - - name: Build Application - run: pnpm build:app - env: - ## increase node.js m memory limit for building - ## with sourcemaps - NODE_OPTIONS: "--max-old-space-size=4096" - - uses: ./.github/actions/setup-playwright - name: Run E2E Tests diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index e37091b48..7401d697d 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,41 @@ # fuels-wallet +## 0.44.0 + +### Minor Changes + +- [#1700](https://github.com/FuelLabs/fuels-wallet/pull/1700) [`be181de6`](https://github.com/FuelLabs/fuels-wallet/commit/be181de610d15f18efa977e183d449ae3f871c54) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - feat: implemented recoverWallet method and in case of disaster recover it + +- [#1700](https://github.com/FuelLabs/fuels-wallet/pull/1700) [`be181de6`](https://github.com/FuelLabs/fuels-wallet/commit/be181de610d15f18efa977e183d449ae3f871c54) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - feat: synchronize db with chrome storage + +- [#1700](https://github.com/FuelLabs/fuels-wallet/pull/1700) [`be181de6`](https://github.com/FuelLabs/fuels-wallet/commit/be181de610d15f18efa977e183d449ae3f871c54) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - feat: create new database and table (outside of react) to identify if only dexiedb database is breaking + +- [#1700](https://github.com/FuelLabs/fuels-wallet/pull/1700) [`be181de6`](https://github.com/FuelLabs/fuels-wallet/commit/be181de610d15f18efa977e183d449ae3f871c54) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - chore: report to sentry when there's a recover happening + +### Patch Changes + +- [#1695](https://github.com/FuelLabs/fuels-wallet/pull/1695) [`89454569`](https://github.com/FuelLabs/fuels-wallet/commit/8945456956dcf4117d1ca0f01ab91c504bb1095e) Thanks [@arthurgeron](https://github.com/arthurgeron)! - Fixed setup page not finding some local assets + +- [#1691](https://github.com/FuelLabs/fuels-wallet/pull/1691) [`33f3d77a`](https://github.com/FuelLabs/fuels-wallet/commit/33f3d77ac8b5bf53be33963e1df3e1a03450b731) Thanks [@nelitow](https://github.com/nelitow)! - ### Changed + + Updated nanoid to the latest versions for better performance and security. + +- [#1692](https://github.com/FuelLabs/fuels-wallet/pull/1692) [`473067c9`](https://github.com/FuelLabs/fuels-wallet/commit/473067c9c3c442b3f51f28c10b99edd1827f1cad) Thanks [@arthurgeron](https://github.com/arthurgeron)! - Updated Biome to 1.9.4 and fixed lint errors. + +- [#1639](https://github.com/FuelLabs/fuels-wallet/pull/1639) [`b0b5f2f5`](https://github.com/FuelLabs/fuels-wallet/commit/b0b5f2f529fe0c9844fa35d41c8f18d0978ca151) Thanks [@arthurgeron](https://github.com/arthurgeron)! - Fixed wallet not loading correctly in local environment on Chrome 130+ + +- [#1704](https://github.com/FuelLabs/fuels-wallet/pull/1704) [`55606980`](https://github.com/FuelLabs/fuels-wallet/commit/55606980a9204d6f21fbb8c290b9b1c6ba2e7dcf) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - chore: reduce reported data to sentry + +- [#1696](https://github.com/FuelLabs/fuels-wallet/pull/1696) [`8b68c21e`](https://github.com/FuelLabs/fuels-wallet/commit/8b68c21e39e7b5b0fbb9791c7d306d536a6d37ad) Thanks [@arthurgeron](https://github.com/arthurgeron)! - Fix exception when injecting content in the hello page + +- [#1695](https://github.com/FuelLabs/fuels-wallet/pull/1695) [`89454569`](https://github.com/FuelLabs/fuels-wallet/commit/8945456956dcf4117d1ca0f01ab91c504bb1095e) Thanks [@arthurgeron](https://github.com/arthurgeron)! - Upgrade Vite to V6 major + +- [#1694](https://github.com/FuelLabs/fuels-wallet/pull/1694) [`cfcf2209`](https://github.com/FuelLabs/fuels-wallet/commit/cfcf220966cafc9bf7cd8861f0421a7560315365) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - chore: update 'dev:crx' script to use default env + +- Updated dependencies [[`473067c9`](https://github.com/FuelLabs/fuels-wallet/commit/473067c9c3c442b3f51f28c10b99edd1827f1cad), [`55606980`](https://github.com/FuelLabs/fuels-wallet/commit/55606980a9204d6f21fbb8c290b9b1c6ba2e7dcf)]: + - @fuels/playwright-utils@0.44.0 + - @fuel-wallet/connections@0.44.0 + ## 0.43.0 ### Minor Changes diff --git a/packages/app/jest.setup.ts b/packages/app/jest.setup.ts index 7de8e6fc5..947361ed3 100644 --- a/packages/app/jest.setup.ts +++ b/packages/app/jest.setup.ts @@ -30,6 +30,35 @@ jest.mock('react-dom/test-utils', () => { }; }); +// Replace chromeStorage +jest.mock('./src/systems/Core/services/chromeStorage', () => { + return { + chromeStorage: { + accounts: { + get: jest.fn(), + getAll: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), + }, + networks: { + get: jest.fn(), + getAll: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), + }, + vaults: { + get: jest.fn(), + getAll: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), + }, + }, + }; +}); + console.warn = jest.fn(); const noop = () => {}; diff --git a/packages/app/load.envs.cts b/packages/app/load.envs.cts index 8a54d6e41..08d15bd80 100644 --- a/packages/app/load.envs.cts +++ b/packages/app/load.envs.cts @@ -1,5 +1,4 @@ import { readFileSync } from 'node:fs'; -import path from 'node:path'; import { resolve } from 'node:path'; import { config } from 'dotenv'; diff --git a/packages/app/package.json b/packages/app/package.json index f1a55a589..52d6a40ee 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,7 +1,7 @@ { "name": "fuels-wallet", "private": true, - "version": "0.43.0", + "version": "0.44.0", "type": "module", "database": "23", "scripts": { diff --git a/packages/app/playwright.config.ts b/packages/app/playwright.config.ts index 977d4b451..24932d420 100644 --- a/packages/app/playwright.config.ts +++ b/packages/app/playwright.config.ts @@ -1,11 +1,8 @@ -import { join } from 'node:path'; -// biome-ignore lint/style/useNodejsImportProtocol: import { type PlaywrightTestConfig, defineConfig, devices, } from '@playwright/test'; -import './load.envs'; import './load.envs.cts'; const PORT = process.env.PORT; diff --git a/packages/app/playwright.crx-lock.config.ts b/packages/app/playwright.crx-lock.config.ts index 6e9f5c710..e7eb45367 100644 --- a/packages/app/playwright.crx-lock.config.ts +++ b/packages/app/playwright.crx-lock.config.ts @@ -1,13 +1,10 @@ -// biome-ignore lint/style/useNodejsImportProtocol: -import { join } from 'path'; import { defineConfig, devices } from '@playwright/test'; import { playwrightConfig } from './playwright.config'; - -const __dirname = new URL('.', import.meta.url).pathname; +import './load.envs.cts'; export default defineConfig({ ...playwrightConfig, - testMatch: join(__dirname, './playwright/crx/lock.test.ts'), + testMatch: 'playwright/crx/lock.test.ts', testIgnore: undefined, projects: [ { diff --git a/packages/app/playwright/crx/crx.test.ts b/packages/app/playwright/crx/crx.test.ts index f69eafb23..e6a1735cc 100644 --- a/packages/app/playwright/crx/crx.test.ts +++ b/packages/app/playwright/crx/crx.test.ts @@ -1,5 +1,5 @@ import type { NetworkData, Account as WalletAccount } from '@fuel-wallet/types'; -import { type Locator, type Page, expect } from '@playwright/test'; +import { type Locator, expect } from '@playwright/test'; import { delay, @@ -10,7 +10,6 @@ import { hasText, reload, seedWallet, - visit, waitAriaLabel, } from '../commons'; import { diff --git a/packages/app/playwright/crx/lock.test.ts b/packages/app/playwright/crx/lock.test.ts index fe442764c..ba9c2d4e9 100644 --- a/packages/app/playwright/crx/lock.test.ts +++ b/packages/app/playwright/crx/lock.test.ts @@ -18,6 +18,18 @@ import { test } from './utils'; test.setTimeout(360_000); test.describe('Lock FuelWallet after inactivity', () => { + test('If user opens popup it should force open a sign-up page', async ({ + context, + extensionId, + }) => { + const popupPage = await context.newPage(); + await popupPage.goto(`chrome-extension://${extensionId}/popup.html`); + const page = await context.waitForEvent('page', { + predicate: (page) => page.url().includes('sign-up'), + }); + expect(page.url()).toContain('sign-up'); + }); + test('should lock the wallet after 1 minute of inactivity (config in .env file)', async ({ context, baseURL, @@ -50,19 +62,7 @@ test.describe('Lock FuelWallet after inactivity', () => { await test.step('Create wallet', async () => { const pages = context.pages(); - let page = pages.find((page) => page.url().includes('sign-up')); - - if (!page) { - page = await context.waitForEvent('page', { - predicate: (page) => page.url().includes('sign-up'), - timeout: 10000, // Adjust timeout as needed - }); - } - - if (!page) { - throw new Error('Sign-up page did not open'); - } - + const [page] = pages.filter((page) => page.url().includes('sign-up')); await reload(page); await getElementByText(page, /Create new wallet/i).click(); @@ -97,6 +97,7 @@ test.describe('Lock FuelWallet after inactivity', () => { /** Account created */ await hasText(page, /Wallet created successfully/i, 0, 15000); + await page.close(); }); @@ -111,10 +112,17 @@ test.describe('Lock FuelWallet after inactivity', () => { await getByAriaLabel(popupPage, 'Accounts').click(); await popupPage.waitForTimeout(65_000); await hasText(popupPage, /Assets/i); + + const pages = context.pages(); + const walletPages = pages.filter((page) => { + return page.url().includes('chrome-extension://'); + }); + for (const page of walletPages) { + await page.close(); + } }); await test.step('Resume auto-lock timer after closing wallet', async () => { - await popupPage.close(); const page = await context.newPage(); await page.waitForTimeout(65_000); await page.goto(`chrome-extension://${extensionId}/popup.html`); diff --git a/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx b/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx index 8250942b1..fb54db563 100644 --- a/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx +++ b/packages/app/src/systems/Account/components/BalanceAssets/BalanceAssets.tsx @@ -29,7 +29,7 @@ export const BalanceAssets = ({ [balances] ); - if (isLoading) return ; + if (isLoading || !balances) return ; const isEmpty = !balances || !balances.length; if (isEmpty) return ; const balancesToShow = balances.filter( @@ -40,6 +40,7 @@ export const BalanceAssets = ({ function toggle() { setShowUnknown((s) => !s); } + return ( {balancesToShow.map((balance) => { @@ -49,7 +50,7 @@ export const BalanceAssets = ({ return ( { const account = useMemo(() => { + if (!address) return ''; if (isContract) return Address.fromDynamicInput(address).toB256(); return Address.fromDynamicInput(address).toString(); }, [isContract, address]); diff --git a/packages/app/src/systems/Account/events.tsx b/packages/app/src/systems/Account/events.tsx index 50868a1bd..528bd7cb0 100644 --- a/packages/app/src/systems/Account/events.tsx +++ b/packages/app/src/systems/Account/events.tsx @@ -4,11 +4,8 @@ import { Services } from '~/store'; export function accountEvents(store: Store) { return { - reloadBalance() { - store.send(Services.accounts, { type: 'RELOAD_BALANCE' }); - }, - updateAccounts() { - store.send(Services.accounts, { type: 'REFRESH_ACCOUNTS' }); + refreshAccounts(input?: { skipLoading?: boolean }) { + store.send(Services.accounts, { type: 'REFRESH_ACCOUNTS', input }); }, setCurrentAccount(account: Account) { store.send(Services.accounts, { diff --git a/packages/app/src/systems/Account/hooks/useAccounts.tsx b/packages/app/src/systems/Account/hooks/useAccounts.tsx index 031c75aaf..e2ee510c0 100644 --- a/packages/app/src/systems/Account/hooks/useAccounts.tsx +++ b/packages/app/src/systems/Account/hooks/useAccounts.tsx @@ -45,9 +45,7 @@ const selectors = { }; const listenerAccountFetcher = () => { - store.send(Services.accounts, { - type: 'REFRESH_ACCOUNT', - }); + store.refreshAccounts({ skipLoading: true }); }; export function useAccounts() { diff --git a/packages/app/src/systems/Account/machines/accountsMachine.tsx b/packages/app/src/systems/Account/machines/accountsMachine.tsx index ed1bd2adc..975bda951 100644 --- a/packages/app/src/systems/Account/machines/accountsMachine.tsx +++ b/packages/app/src/systems/Account/machines/accountsMachine.tsx @@ -11,13 +11,17 @@ import type { AccountInputs } from '../services/account'; type MachineContext = { accounts?: Account[]; + needsRecovery?: boolean; account?: AccountWithBalance; error?: unknown; }; type MachineServices = { fetchAccounts: { - data: Account[]; + data: { + accounts: Account[]; + needsRecovery: boolean; + }; }; fetchAccount: { data: AccountWithBalance; @@ -28,9 +32,7 @@ type MachineServices = { }; export type AccountsMachineEvents = - | { type: 'REFRESH_ACCOUNT'; input?: null } - | { type: 'REFRESH_ACCOUNTS'; input?: null } - | { type: 'RELOAD_BALANCE'; input?: null } + | { type: 'REFRESH_ACCOUNTS'; input?: { skipLoading?: boolean } } | { type: 'SET_CURRENT_ACCOUNT'; input: AccountInputs['setCurrentAccount'] } // biome-ignore lint/suspicious/noConfusingVoidType: | { type: 'LOGOUT'; input?: void } @@ -39,26 +41,69 @@ export type AccountsMachineEvents = input: AccountInputs['updateAccount']; }; -const fetchAccount = { - invoke: { - src: 'fetchAccount', - onDone: [ - { - target: 'idle', - actions: ['assignAccount'], - cond: 'hasAccount', - }, - { - target: 'idle', - actions: ['assignAccount'], +const fetchingAccountsState = +{ + initial: 'fetchingAccounts', + states: { + fetchingAccounts: { + invoke: { + src: 'fetchAccounts', + onDone: [ + { + target: 'recoveringWallet', + actions: ['assignAccounts', 'setIsLogged'], + cond: 'hasAccountsOrNeedsRecovery', + }, + { + target: 'fetchingAccount', + actions: ['assignAccounts'], + }, + ], + onError: [ + { + actions: 'assignError', + target: '#(machine).failed', + }, + ], + }, + }, + recoveringWallet: { + invoke: { + src: 'recoverWallet', + onDone: [ + { + actions: 'assignError', + target: '#(machine).failed', + cond: FetchMachine.hasError, + }, + { + target: 'fetchingAccount', + }, + ], }, - ], - onError: [ - { - actions: 'assignError', - target: 'failed', + }, + fetchingAccount: { + invoke: { + src: 'fetchAccount', + onDone: [ + { + cond: FetchMachine.hasError, + actions: 'assignError', + target: '#(machine).failed', + }, + { + target: '#(machine).idle', + actions: ['assignAccount'], + }, + ], + onError: [ + { + actions: 'assignError', + target: '#(machine).failed', + }, + ], }, - ], + }, }, }; @@ -89,44 +134,17 @@ export const accountsMachine = createMachine( * Update accounts every 5 seconds */ TIMEOUT: { - target: 'refreshAccount', + target: 'refreshingAccounts', cond: 'isLoggedIn', }, }, }, fetchingAccounts: { tags: ['loading'], - invoke: { - src: 'fetchAccounts', - onDone: [ - { - target: 'fetchingAccount', - actions: ['assignAccounts', 'setIsLogged'], - cond: 'hasAccounts', - }, - { - target: 'idle', - actions: ['assignAccounts'], - }, - ], - onError: [ - { - actions: 'assignError', - target: 'failed', - }, - ], - }, - }, - fetchingAccount: { - tags: ['loading'], - ...fetchAccount, + ...fetchingAccountsState, }, - refreshAccount: { - ...fetchAccount, - }, - reloadingBalance: { - tags: ['loading'], - ...fetchAccount, + refreshingAccounts: { + ...fetchingAccountsState, }, settingCurrentAccount: { invoke: { @@ -177,16 +195,15 @@ export const accountsMachine = createMachine( LOGOUT: { target: 'loggingout', }, - REFRESH_ACCOUNTS: { - target: 'fetchingAccounts', - }, - REFRESH_ACCOUNT: { - target: 'refreshAccount', - }, - RELOAD_BALANCE: { - target: 'reloadingBalance', - actions: ['notifyUpdateAccounts'], - }, + REFRESH_ACCOUNTS: [ + { + cond: 'shouldSkipLoading', + target: 'refreshingAccounts', + }, + { + target: 'fetchingAccounts', + } + ], }, }, { @@ -196,7 +213,8 @@ export const accountsMachine = createMachine( }, actions: { assignAccounts: assign({ - accounts: (_, ev) => ev.data, + accounts: (_, ev) => ev.data.accounts, + needsRecovery: (_, ev) => ev.data.needsRecovery, }), assignAccount: assign({ account: (_, ev) => ev.data, @@ -212,17 +230,31 @@ export const accountsMachine = createMachine( Storage.setItem(IS_LOGGED_KEY, true); }, notifyUpdateAccounts: () => { - store.updateAccounts(); + store.refreshAccounts(); }, redirectToHome: () => { store.closeOverlay(); - }, + } }, services: { - fetchAccounts: FetchMachine.create({ + fetchAccounts: FetchMachine.create< + never, + { accounts: Account[]; needsRecovery: boolean } + >({ showError: true, async fetch() { - return AccountService.getAccounts(); + const accounts = await AccountService.getAccounts(); + const { needsRecovery } = await AccountService.fetchRecoveryState(); + + return { + accounts, + needsRecovery, + }; + }, + }), + recoverWallet: FetchMachine.create({ + async fetch() { + await AccountService.recoverWallet(); }, }), fetchAccount: FetchMachine.create({ @@ -235,10 +267,9 @@ export const accountsMachine = createMachine( accountToFetch = await AccountService.getCurrentAccount(); } if (!accountToFetch) return undefined; + const selectedNetwork = await NetworkService.getSelectedNetwork(); - if (!selectedNetwork) { - throw new Error('No selected network'); - } + if (!selectedNetwork) return undefined; const providerUrl = selectedNetwork.url; const accountWithBalance = await AccountService.fetchBalance({ @@ -277,11 +308,17 @@ export const accountsMachine = createMachine( isLoggedIn: () => { return !!Storage.getItem(IS_LOGGED_KEY); }, - hasAccount: (ctx, ev) => { - return Boolean(ev?.data || ctx?.account); + hasAccountsOrNeedsRecovery: (ctx, ev) => { + const hasAccounts = Boolean( + (ev.data.accounts || ctx?.accounts || []).length + ); + const needsRecovery = Boolean( + ev.data.needsRecovery || ctx?.needsRecovery + ); + return hasAccounts || needsRecovery; }, - hasAccounts: (ctx, ev) => { - return Boolean((ev.data || ctx?.accounts || []).length); + shouldSkipLoading: (_, ev) => { + return !!ev.input?.skipLoading; }, }, } diff --git a/packages/app/src/systems/Account/machines/addAccountMachine.tsx b/packages/app/src/systems/Account/machines/addAccountMachine.tsx index 401688f50..f94ff881b 100644 --- a/packages/app/src/systems/Account/machines/addAccountMachine.tsx +++ b/packages/app/src/systems/Account/machines/addAccountMachine.tsx @@ -74,7 +74,7 @@ export const addAccountMachine = createMachine( { actions: { notifyUpdateAccounts: () => { - store.updateAccounts(); + store.refreshAccounts(); }, redirectToHome() { store.closeOverlay(); diff --git a/packages/app/src/systems/Account/machines/editAccountMachine.tsx b/packages/app/src/systems/Account/machines/editAccountMachine.tsx index 3bfc66ee4..8e8a15f11 100644 --- a/packages/app/src/systems/Account/machines/editAccountMachine.tsx +++ b/packages/app/src/systems/Account/machines/editAccountMachine.tsx @@ -97,7 +97,7 @@ export const editAccountMachine = createMachine( account: (_, ev) => ev.data, }), notifyUpdateAccounts: () => { - store.updateAccounts(); + store.refreshAccounts({ skipLoading: true }); }, redirectToList() { store.openAccountList(); diff --git a/packages/app/src/systems/Account/machines/importAccountMachine.tsx b/packages/app/src/systems/Account/machines/importAccountMachine.tsx index 2bb1b758d..1c3431dcb 100644 --- a/packages/app/src/systems/Account/machines/importAccountMachine.tsx +++ b/packages/app/src/systems/Account/machines/importAccountMachine.tsx @@ -68,7 +68,7 @@ export const importAccountMachine = createMachine( { actions: { notifyUpdateAccounts: () => { - store.updateAccounts(); + store.refreshAccounts(); }, redirectToHome() { store.closeOverlay(); diff --git a/packages/app/src/systems/Account/services/account.ts b/packages/app/src/systems/Account/services/account.ts index 8a86ea2e7..0cba8114f 100644 --- a/packages/app/src/systems/Account/services/account.ts +++ b/packages/app/src/systems/Account/services/account.ts @@ -5,13 +5,14 @@ import type { AccountWithBalance, CoinAsset, } from '@fuel-wallet/types'; +import * as Sentry from '@sentry/react'; import { Address, type Provider, bn } from 'fuels'; import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; -import { AssetService } from '~/systems/Asset/services'; -import { getFuelAssetByAssetId } from '~/systems/Asset/utils'; +import { chromeStorage } from '~/systems/Core/services/chromeStorage'; import type { Maybe } from '~/systems/Core/types'; import { db } from '~/systems/Core/utils/database'; import { getUniqueString } from '~/systems/Core/utils/string'; +import { getTestNoDexieDbData } from '../utils/getTestNoDexieDbData'; export type AccountInputs = { addAccount: { @@ -101,6 +102,7 @@ export class AccountService { } const { account, providerUrl } = input; + try { const provider = await createProvider(providerUrl!); const balances = await getBalances(provider, account.publicKey); @@ -194,8 +196,7 @@ export class AccountService { }); } - static setCurrentAccountToDefault() { - console.log('recovering default'); + static async setCurrentAccountToDefault() { return db.transaction('rw', db.accounts, async () => { const [firstAccount] = await db.accounts.toArray(); if (firstAccount) { @@ -206,6 +207,117 @@ export class AccountService { }); } + static async fetchRecoveryState() { + const [ + backupAccounts, + allAccounts, + backupVaults, + allVaults, + backupNetworks, + allNetworks, + ] = await Promise.all([ + chromeStorage.accounts.getAll(), + db.accounts.toArray(), + chromeStorage.vaults.getAll(), + db.vaults.toArray(), + chromeStorage.networks.getAll(), + db.networks.toArray(), + ]); + + // if there is no accounts, means the user lost it. try recovering it + const needsAccRecovery = + allAccounts?.length === 0 && backupAccounts?.length > 0; + const needsVaultRecovery = + allVaults?.length === 0 && backupVaults?.length > 0; + const needsNetworkRecovery = + allNetworks?.length === 0 && backupNetworks?.length > 0; + const needsRecovery = + needsAccRecovery || needsVaultRecovery || needsNetworkRecovery; + + return { + backupAccounts, + backupVaults, + backupNetworks, + needsRecovery, + needsAccRecovery, + needsVaultRecovery, + needsNetworkRecovery, + }; + } + + static async recoverWallet() { + const { + backupAccounts, + backupVaults, + backupNetworks, + needsRecovery, + needsAccRecovery, + needsVaultRecovery, + needsNetworkRecovery, + } = await AccountService.fetchRecoveryState(); + + if (needsRecovery) { + (async () => { + // biome-ignore lint/suspicious/noExplicitAny: + const dataToLog: any = {}; + try { + dataToLog.backupAccounts = JSON.stringify(backupAccounts?.map((account) => account?.data?.address) || []); + dataToLog.backupNetworks = JSON.stringify(backupNetworks || []); + // try getting data from indexedDB (outside of dexie) to check if it's also corrupted + const testNoDexieDbData = await getTestNoDexieDbData(); + dataToLog.testNoDexieDbData = testNoDexieDbData; + } catch (_) {} + + Sentry.captureException( + 'Disaster on DB. Start recovering accounts / vaults / networks', + { + extra: dataToLog, + tags: { manual: true }, + } + ); + })(); + + await db.transaction( + 'rw', + db.accounts, + db.vaults, + db.networks, + async () => { + if (needsAccRecovery) { + let isCurrentFlag = true; + console.log('recovering accounts', backupAccounts); + for (const account of backupAccounts) { + // in case of recovery, the first account will be the current + if (account.key && account.data.address) { + await db.accounts.add({ + ...account.data, + isCurrent: isCurrentFlag, + }); + isCurrentFlag = false; + } + } + } + if (needsVaultRecovery) { + console.log('recovering vaults', backupVaults); + for (const vault of backupVaults) { + if (vault.key && vault.data) { + await db.vaults.add(vault.data); + } + } + } + if (needsNetworkRecovery) { + console.log('recovering networks', backupNetworks); + for (const network of backupNetworks) { + if (network.key && network.data.id) { + await db.networks.add(network.data); + } + } + } + } + ); + } + } + static setCurrentAccount(input: AccountInputs['setCurrentAccount']) { return db.transaction('rw', db.accounts, async () => { await db.accounts diff --git a/packages/app/src/systems/Account/utils/getTestNoDexieDbData.ts b/packages/app/src/systems/Account/utils/getTestNoDexieDbData.ts new file mode 100644 index 000000000..c84747aaf --- /dev/null +++ b/packages/app/src/systems/Account/utils/getTestNoDexieDbData.ts @@ -0,0 +1,23 @@ +export const getTestNoDexieDbData = () => + new Promise((resolve, reject) => { + async function getData() { + const request = await window.indexedDB.open('TestDatabase'); + request.onsuccess = (event: Event) => { + const db = (event.target as IDBRequest).result as IDBDatabase; + const tx = db.transaction('myTable', 'readonly'); + const store = tx.objectStore('myTable'); + + // Get all the data from the object store + const request = store.getAll(); + request.onsuccess = (event: Event) => { + const data = (event.target as IDBRequest).result; + resolve(data); + }; + }; + } + getData(); + + setTimeout(() => { + reject(new Error('Timeout')); + }, 10000); + }); diff --git a/packages/app/src/systems/Account/utils/storage.tsx b/packages/app/src/systems/Account/utils/storage.tsx index 06c12378b..7c418c900 100644 --- a/packages/app/src/systems/Account/utils/storage.tsx +++ b/packages/app/src/systems/Account/utils/storage.tsx @@ -1,8 +1,12 @@ import type { StorageAbstract } from 'fuels'; +import { chromeStorage } from '~/systems/Core/services/chromeStorage'; import { db } from '~/systems/Core/utils/database'; export class IndexedDBStorage implements StorageAbstract { async getItem(key: string) { + const vault = await chromeStorage.vaults.get({ key }); + if (vault?.data?.data) return vault?.data?.data; + return db.transaction('r', db.vaults, async () => { const vault = await db.vaults.get({ key }); return vault?.data; @@ -10,18 +14,22 @@ export class IndexedDBStorage implements StorageAbstract { } async setItem(key: string, data: string) { + await chromeStorage.vaults.set({ key, data: { key, data } }); await db.transaction('rw', db.vaults, db.accounts, async () => { await db.vaults.put({ key, data }); }); } async removeItem(key: string) { + await chromeStorage.vaults.remove({ key }); await db.transaction('rw', db.vaults, db.accounts, async () => { await db.vaults.where({ key }).delete(); }); } async clear() { + await chromeStorage.vaults.clear(); + await chromeStorage.accounts.clear(); await db.transaction('rw', db.vaults, db.accounts, async () => { await db.vaults.clear(); await db.accounts.clear(); diff --git a/packages/app/src/systems/Asset/machines/assetsMachine.tsx b/packages/app/src/systems/Asset/machines/assetsMachine.tsx index 2a2169492..6c459f201 100644 --- a/packages/app/src/systems/Asset/machines/assetsMachine.tsx +++ b/packages/app/src/systems/Asset/machines/assetsMachine.tsx @@ -179,7 +179,7 @@ export const assetsMachine = createMachine( toast.success('Asset added successfully'); }, notifyUpdateAccounts: () => { - store.updateAccounts(); + store.refreshAccounts({ skipLoading: true }); }, }, services: { diff --git a/packages/app/src/systems/CRX/background/actions/onInstall.ts b/packages/app/src/systems/CRX/background/actions/onInstall.ts index f6560abef..fb22b742a 100644 --- a/packages/app/src/systems/CRX/background/actions/onInstall.ts +++ b/packages/app/src/systems/CRX/background/actions/onInstall.ts @@ -5,16 +5,7 @@ import { executeContentScript } from '../../scripts/executeContentScript'; executeContentScript(); chrome.runtime.onInstalled.addListener(async (object) => { - const { shouldRecoverWelcomeFromError } = await chrome.storage.local.get( - 'shouldRecoverWelcomeFromError' - ); - - chrome.storage.local.remove('shouldRecoverWelcomeFromError'); - - if ( - shouldRecoverWelcomeFromError || - object.reason === chrome.runtime.OnInstalledReason.INSTALL - ) { + if (object.reason === chrome.runtime.OnInstalledReason.INSTALL) { chrome.tabs.create({ url: welcomeLink() }); } }); diff --git a/packages/app/src/systems/CRX/background/services/DatabaseEvents.ts b/packages/app/src/systems/CRX/background/services/DatabaseEvents.ts index 347f9e2a4..e126d33bb 100644 --- a/packages/app/src/systems/CRX/background/services/DatabaseEvents.ts +++ b/packages/app/src/systems/CRX/background/services/DatabaseEvents.ts @@ -17,6 +17,7 @@ import { } from 'fuels'; import type { CommunicationProtocol } from './CommunicationProtocol'; import { DatabaseObservable } from './DatabaseObservable'; +import { chromeStorage } from '~/systems/Core/services/chromeStorage'; export class DatabaseEvents { readonly databaseObservable: DatabaseObservable< @@ -74,6 +75,34 @@ export class DatabaseEvents { } ); + // -- START Events for sync db with chrome storage + this.databaseObservable.on<'accounts:create', Account>( + 'accounts:create', + async (event) => { + const currentAccount = event.obj; + if (currentAccount) { + await chromeStorage.accounts.set({ + key: currentAccount.address, + data: currentAccount, + }); + } + } + ); + this.databaseObservable.on<'accounts:update', Account>( + 'accounts:update', + async (event) => { + const currentAccount = event.obj; + + if (currentAccount) { + await chromeStorage.accounts.set({ + key: currentAccount.address, + data: currentAccount, + }); + } + } + ); + // -- END Events for sync db with chrome storage + this.databaseObservable.on<'accounts:update', Account>( 'accounts:update', async (updateEvent) => { diff --git a/packages/app/src/systems/CRX/background/services/VaultService.ts b/packages/app/src/systems/CRX/background/services/VaultService.ts index e757118e2..e8530c5d2 100644 --- a/packages/app/src/systems/CRX/background/services/VaultService.ts +++ b/packages/app/src/systems/CRX/background/services/VaultService.ts @@ -135,8 +135,7 @@ export class VaultService extends VaultServer { if (eventType === 'DB_EVENT' && payload.event === 'restarted') { if (!integrity) { - chrome.storage.local.set({ shouldRecoverWelcomeFromError: true }); - return this.resetAndReload(); + return this.reload(); } } diff --git a/packages/app/src/systems/CRX/scripts/executeContentScript.ts b/packages/app/src/systems/CRX/scripts/executeContentScript.ts index 292c29b19..8f4dc262f 100644 --- a/packages/app/src/systems/CRX/scripts/executeContentScript.ts +++ b/packages/app/src/systems/CRX/scripts/executeContentScript.ts @@ -34,7 +34,10 @@ export async function executeContentScript() { } function injectContentScript(tabId: number) { - const env = process.env?.NODE_ENV; + let env: string | undefined = undefined; + if (typeof process !== 'undefined') { + env = process?.env?.NODE_ENV; + } chrome.scripting .executeScript({ target: { tabId: tabId, allFrames: true }, diff --git a/packages/app/src/systems/Core/services/chromeStorage.ts b/packages/app/src/systems/Core/services/chromeStorage.ts new file mode 100644 index 000000000..c2eab0b9e --- /dev/null +++ b/packages/app/src/systems/Core/services/chromeStorage.ts @@ -0,0 +1,87 @@ +import type { Account, NetworkData, Vault } from '@fuel-wallet/types'; + +interface ChromeStorageRow { + key: string; + data: T; +} + +class ChromeStorageTable { + constructor(private readonly tableName: string) { + this.tableName = tableName; + } + + async get({ key }: { key: string }) { + const rowsMap = (await chrome?.storage?.local?.get(this.tableName)) || {}; + const rows = rowsMap[this.tableName] || []; + + let foundIndex = -1; + for (let i = 0; i < rows.length; i++) { + if (rows[i].key === key) { + foundIndex = i; + break; + } + } + const found = rows[foundIndex]?.data; + + return { + data: found, + key, + index: foundIndex, + rows, + }; + } + + async getAll(): Promise[]> { + const rowsMap = (await chrome?.storage?.local?.get(this.tableName)) || {}; + const rows: ChromeStorageRow[] = rowsMap[this.tableName] || []; + return rows; + } + + async set({ key, data }: ChromeStorageRow) { + const { index, rows } = await this.get({ key }); + + // update + if (index !== -1) { + rows[index] = { + key, + data, + }; + } else { + // create + rows.push({ + key, + data, + }); + } + + await chrome?.storage?.local?.set({ + [this.tableName]: rows, + }); + } + + async remove({ key }: { key: string }) { + const { index, rows } = await this.get({ key }); + + if (index !== -1) { + rows.splice(index, 1); + await chrome?.storage?.local?.set({ + [this.tableName]: rows, + }); + } + } + + async clear() { + await chrome?.storage?.local?.set({ + [this.tableName]: [], + }); + } +} + +export const chromeStorage = { + accounts: new ChromeStorageTable('accounts'), + networks: new ChromeStorageTable('networks'), + vaults: new ChromeStorageTable('vaults'), + clear: () => { + chrome?.storage?.local?.clear(); + } +}; diff --git a/packages/app/src/systems/Core/services/core.ts b/packages/app/src/systems/Core/services/core.ts index ad52adcf5..f6bbbd650 100644 --- a/packages/app/src/systems/Core/services/core.ts +++ b/packages/app/src/systems/Core/services/core.ts @@ -3,12 +3,14 @@ import { VaultService } from '~/systems/Vault'; import { delay } from '../utils'; import { db } from '../utils/database'; import { Storage } from '../utils/storage'; +import { chromeStorage } from './chromeStorage'; // biome-ignore lint/complexity/noStaticOnlyClass: export class CoreService { static async clear() { toast.success('Your wallet will be reset'); await delay(1500); + await chromeStorage.clear(); await VaultService.clear(); await db.clear(); await Storage.clear(); diff --git a/packages/app/src/systems/Core/services/index.ts b/packages/app/src/systems/Core/services/index.ts index 4b0e04137..57a466133 100644 --- a/packages/app/src/systems/Core/services/index.ts +++ b/packages/app/src/systems/Core/services/index.ts @@ -1 +1,2 @@ export * from './core'; +export * from './chromeStorage'; diff --git a/packages/app/src/systems/Core/utils/database.ts b/packages/app/src/systems/Core/utils/database.ts index 489297ae2..ac2362954 100644 --- a/packages/app/src/systems/Core/utils/database.ts +++ b/packages/app/src/systems/Core/utils/database.ts @@ -12,6 +12,7 @@ import Dexie, { type DbEvents, type PromiseExtended, type Table } from 'dexie'; import 'dexie-observable'; import type { AssetFuel } from 'fuels'; import type { TransactionCursor } from '~/systems/Transaction'; +import { chromeStorage } from '../services/chromeStorage'; import { applyDbVersioning } from './databaseVersioning'; type FailureEvents = Extract; @@ -43,10 +44,75 @@ export class FuelDB extends Dexie { this.on('close', () => this.restart('close')); } + async createParallelDb() { + // add table outside of dexie to test if it will be corrupted also with dexie FuelDB + if (typeof window !== "undefined") { + const request = await window.indexedDB.open('TestDatabase', 2); + request.onupgradeneeded = (event) => { + const db = (event.target as IDBRequest)?.result; + db.createObjectStore('myTable', { keyPath: 'id' }); + }; + request.onsuccess = (event: Event) => { + const db = (event.target as IDBRequest).result as IDBDatabase; + const tx = db.transaction('myTable', 'readwrite'); + const store = tx.objectStore('myTable'); + + const countRequest = store.count(); + countRequest.onsuccess = () => { + if (countRequest.result === 0) { + store.add({ id: 1, name: 'John' }); + } + }; + }; + } + } + + async syncDbToChromeStorage() { + const accounts = await this.accounts.toArray(); + const vaults = await this.vaults.toArray(); + const networks = await this.networks.toArray(); + + // @TODO: this is a temporary solution to avoid the storage accounts of being wrong and + // users losing funds in case of no backup + // if has account, save to chrome storage + if (accounts.length) { + for (const account of accounts) { + await chromeStorage.accounts.set({ + key: account.address, + data: account, + }); + } + } + if (vaults.length) { + for (const vault of vaults) { + await chromeStorage.vaults.set({ + key: vault.key, + data: vault, + }); + } + } + if (networks.length) { + for (const network of networks) { + await chromeStorage.networks.set({ + key: network.id || '', + data: network, + }); + } + } + } + open(): PromiseExtended { try { return super.open().then((res) => { this.restartAttempts = 0; + try { + (() => this.createParallelDb())(); + } catch(_){} + + try { + (() => this.syncDbToChromeStorage())(); + } catch(_){} + return res; }); } catch (err) { diff --git a/packages/app/src/systems/Core/utils/databaseVersioning.ts b/packages/app/src/systems/Core/utils/databaseVersioning.ts index 8477ff938..b4f642bd5 100644 --- a/packages/app/src/systems/Core/utils/databaseVersioning.ts +++ b/packages/app/src/systems/Core/utils/databaseVersioning.ts @@ -232,6 +232,9 @@ export const applyDbVersioning = (db: Dexie) => { abis: '&contractId', errors: '&id', }); + + // DB VERSION 28 + // add fetchedAt column to indexedAssets table db.version(28).stores({ vaults: 'key', accounts: '&address, &name', diff --git a/packages/app/src/systems/DApp/machines/selectNetworkRequestMachine.tsx b/packages/app/src/systems/DApp/machines/selectNetworkRequestMachine.tsx index 1efa46f40..79fec3f63 100644 --- a/packages/app/src/systems/DApp/machines/selectNetworkRequestMachine.tsx +++ b/packages/app/src/systems/DApp/machines/selectNetworkRequestMachine.tsx @@ -115,7 +115,7 @@ export const selectNetworkRequestMachine = createMachine( store.refreshNetworks(); }, notifyUpdateAccounts: () => { - store.updateAccounts(); + store.refreshAccounts(); }, }, services: { diff --git a/packages/app/src/systems/Home/pages/Home/Home.tsx b/packages/app/src/systems/Home/pages/Home/Home.tsx index 93e60fba1..bc6c0e0b6 100644 --- a/packages/app/src/systems/Home/pages/Home/Home.tsx +++ b/packages/app/src/systems/Home/pages/Home/Home.tsx @@ -43,7 +43,7 @@ export function Home() { diff --git a/packages/app/src/systems/Network/machines/networksMachine.ts b/packages/app/src/systems/Network/machines/networksMachine.ts index 62f91cca7..d35706922 100644 --- a/packages/app/src/systems/Network/machines/networksMachine.ts +++ b/packages/app/src/systems/Network/machines/networksMachine.ts @@ -319,7 +319,7 @@ export const networksMachine = createMachine( }, }), notifyUpdateAccounts: () => { - store.updateAccounts(); + store.refreshAccounts(); }, assignChainInfo: assign({ chainInfoToAdd: (_, ev) => { diff --git a/packages/app/src/systems/SignUp/machines/signUpMachine.ts b/packages/app/src/systems/SignUp/machines/signUpMachine.ts index 5651629fa..5e5846b63 100644 --- a/packages/app/src/systems/SignUp/machines/signUpMachine.ts +++ b/packages/app/src/systems/SignUp/machines/signUpMachine.ts @@ -228,7 +228,7 @@ export const signUpMachine = createMachine( // External actions sendAccountCreated: () => { Storage.setItem(IS_LOGGED_KEY, true); - store.updateAccounts(); + store.refreshAccounts(); }, redirectToWalletCreated() {}, }, diff --git a/packages/app/src/systems/Vault/services/VaultServer.ts b/packages/app/src/systems/Vault/services/VaultServer.ts index 176c33e51..2ada8b89c 100644 --- a/packages/app/src/systems/Vault/services/VaultServer.ts +++ b/packages/app/src/systems/Vault/services/VaultServer.ts @@ -209,10 +209,6 @@ export class VaultServer extends EventEmitter { async reload() { chrome.runtime.reload(); } - - async resetAndReload() { - return this.reload(); - } } export type VaultMethods = { diff --git a/packages/connections/CHANGELOG.md b/packages/connections/CHANGELOG.md index 438ef5db5..cc654aa32 100644 --- a/packages/connections/CHANGELOG.md +++ b/packages/connections/CHANGELOG.md @@ -1,5 +1,7 @@ # @fuel-wallet/connections +## 0.44.0 + ## 0.43.0 ## 0.42.3 diff --git a/packages/connections/package.json b/packages/connections/package.json index 33335393a..a608d8d85 100644 --- a/packages/connections/package.json +++ b/packages/connections/package.json @@ -2,7 +2,7 @@ "name": "@fuel-wallet/connections", "private": true, "description": "Fuel Wallet Connections for CRX.", - "version": "0.43.0", + "version": "0.44.0", "license": "Apache-2.0", "main": "src/index.ts", "publishConfig": { diff --git a/packages/playwright-utils/CHANGELOG.md b/packages/playwright-utils/CHANGELOG.md index 377e473e9..7d6bed778 100644 --- a/packages/playwright-utils/CHANGELOG.md +++ b/packages/playwright-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @fuels/playwright-utils +## 0.44.0 + +### Patch Changes + +- [#1692](https://github.com/FuelLabs/fuels-wallet/pull/1692) [`473067c9`](https://github.com/FuelLabs/fuels-wallet/commit/473067c9c3c442b3f51f28c10b99edd1827f1cad) Thanks [@arthurgeron](https://github.com/arthurgeron)! - Updated Biome to 1.9.4 and fixed lint errors. + +- [#1704](https://github.com/FuelLabs/fuels-wallet/pull/1704) [`55606980`](https://github.com/FuelLabs/fuels-wallet/commit/55606980a9204d6f21fbb8c290b9b1c6ba2e7dcf) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - chore: update wallet version + ## 0.43.0 ## 0.42.3 diff --git a/packages/playwright-utils/package.json b/packages/playwright-utils/package.json index 17213702c..077995df5 100644 --- a/packages/playwright-utils/package.json +++ b/packages/playwright-utils/package.json @@ -1,6 +1,6 @@ { "name": "@fuels/playwright-utils", - "version": "0.43.0", + "version": "0.44.0", "license": "Apache-2.0", "main": "src/index.ts", "type": "module", diff --git a/packages/playwright-utils/src/fixtures/fixtures.ts b/packages/playwright-utils/src/fixtures/fixtures.ts index 3ba35340e..34f69b1de 100644 --- a/packages/playwright-utils/src/fixtures/fixtures.ts +++ b/packages/playwright-utils/src/fixtures/fixtures.ts @@ -9,7 +9,7 @@ export const test = base.extend<{ pathToExtension: string; fuelWalletVersion: string; }>({ - fuelWalletVersion: '0.21.0', + fuelWalletVersion: '0.42.3', pathToExtension: async ({ fuelWalletVersion }, use) => { const fuelPath = await downloadFuel(fuelWalletVersion); await use(fuelPath); diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index a42ffc523..be6e74e9a 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,7 @@ # @fuel-wallet/types +## 0.44.0 + ## 0.43.0 ## 0.42.3 diff --git a/packages/types/package.json b/packages/types/package.json index 2c62063b2..00e06673a 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,7 +1,7 @@ { "name": "@fuel-wallet/types", "private": true, - "version": "0.43.0", + "version": "0.44.0", "license": "Apache-2.0", "main": "src/index.ts", "publishConfig": {